00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038
00039 #include "Component/ModelManager.H"
00040 #include "Image/ColorOps.H"
00041 #include "Image/CutPaste.H"
00042 #include "Image/DrawOps.H"
00043 #include "Image/Image.H"
00044 #include "Image/ShapeOps.H"
00045 #include "Psycho/PsychoDisplay.H"
00046 #include "Psycho/EyeTrackerConfigurator.H"
00047 #include "Psycho/EyeTracker.H"
00048 #include "Psycho/ClassicSearchItem.H"
00049 #include "Psycho/SearchArray.H"
00050 #include "Component/EventLog.H"
00051 #include "Raster/Raster.H"
00052 #include "Util/StringUtil.H"
00053 #include "GUI/GUIOpts.H"
00054
00055 #include <sstream>
00056 #include <ctime>
00057 #include <vector>
00058 #include <string>
00059
00060 using namespace std;
00061
00062
00063
00064
00065
00066
00067 enum NoiseColor { WHITE, PINK, BROWN };
00068 struct trialAgenda
00069 {
00070 bool repeated;
00071 NoiseColor color;
00072 geom::vec2d targetloc;
00073 uint noiseSeed[3];
00074 Dims scrsize;
00075 trialAgenda(const bool r, const NoiseColor c,
00076 const geom::vec2d t, Dims d)
00077 {
00078 repeated = r;
00079 color = c;
00080 targetloc = t;
00081 scrsize = d;
00082 randomizeNoise(1);
00083 }
00084 trialAgenda()
00085 { }
00086
00087 std::string colname() const
00088 {
00089 switch(color) {
00090 case WHITE: return "white";
00091 case PINK: return "pink";
00092 case BROWN: return "brown";
00093 }
00094 return "";
00095 }
00096
00097 void randomizeNoise(const uint Nbkgds)
00098 {
00099 for(uint i = 0; i < 3; i++) noiseSeed[i] = randomUpToNotIncluding(Nbkgds)+1;
00100 }
00101
00102 std::string backgroundFile(const uint comp) const
00103 {
00104
00105 std::string stimdir = "/lab/jshen/projects/eye-cuing/stimuli/noiseseeds";
00106 std::string backFile = sformat("%s/%s%03d.png",stimdir.c_str(),colname().c_str(),noiseSeed[comp]);
00107
00108
00109 if(!Raster::fileExists(backFile))
00110 {
00111
00112
00113 LFATAL("Image file %s not found.", backFile.c_str());
00114 }
00115 return backFile;
00116 }
00117
00118 Image<PixRGB<byte> > generateBkgd() {
00119 return colorizedBkgd();
00120 }
00121 Image<PixRGB<byte> > colorizedBkgd() {
00122 std::vector<Image<byte> > comp;
00123
00124 for(uint i = 0; i < 3; i++) {
00125
00126 Image<PixRGB<byte> > tmp = Raster::ReadRGB(backgroundFile(i));
00127 comp.push_back(getPixelComponentImage(tmp,i));
00128 }
00129 Image<PixRGB<byte> > RGBnoise = crop(makeRGB(comp[0],comp[1],comp[2]),Point2D<int>(0,0),scrsize);
00130
00131
00132 RGBnoise = RGBnoise/2 + PixRGB<byte>(64,64,64);
00133 return RGBnoise;
00134 }
00135 };
00136
00137 std::string convertToString(const trialAgenda& val)
00138 {
00139 std::stringstream s;
00140 s << val.colname() << " noise, ";
00141
00142 if (val.repeated)
00143 s << "repeated, seed (";
00144 else
00145 s << "random, seed (";
00146 for(uint i = 0; i < 3; i++)
00147 s << val.noiseSeed[i] << (i<2 ? "," : ")");
00148
00149
00150 s << ", target @ (" << val.targetloc.x() + val.scrsize.w()/2 << "," << val.targetloc.y() + val.scrsize.h()/2 << ")";
00151 return s.str();
00152 }
00153
00154
00155
00156
00157 int randomInRange(const int x, const int y)
00158 {
00159 return randomUpToNotIncluding(y-x-1)+(x+1);
00160 }
00161
00162
00163 geom::vec2d randomPtIn(const Rectangle d)
00164 {
00165 return geom::vec2d(randomInRange(d.left(),d.rightO()),
00166 randomInRange(d.top(),d.bottomO()));
00167 }
00168
00169
00170
00171
00172 static int submain(const int argc, char** argv)
00173 {
00174 MYLOGVERB = LOG_INFO;
00175
00176
00177 ModelManager manager("Psycho Visual Search - cuing");
00178
00179 nub::soft_ref<EventLog> el(new EventLog(manager));
00180 manager.addSubComponent(el);
00181
00182 nub::soft_ref<EyeTrackerConfigurator>
00183 etc(new EyeTrackerConfigurator(manager));
00184 manager.addSubComponent(etc);
00185
00186 nub::soft_ref<PsychoDisplay> d(new PsychoDisplay(manager));
00187 manager.addSubComponent(d);
00188
00189
00190 if (manager.parseCommandLine(argc, argv, "<Nblocks> <setSize>",2,2) == false)
00191 return(1);
00192
00193
00194 nub::soft_ref<EyeTracker> et = etc->getET();
00195 d->setEyeTracker(et);
00196 d->setEventLog(el);
00197 et->setEventLog(el);
00198
00199
00200 if (etc->getModelParamString("EyeTrackerType").compare("EL") == 0)
00201 d->setModelParamVal("SDLslaveMode", true);
00202
00203
00204 manager.start();
00205
00206
00207
00208 char keyleft, keyright;
00209 do {
00210 d->pushEvent("===== key for T pointed left =====");
00211 d->displayText("Press key for response for T pointed left:");
00212 keyleft = d->waitForKey();
00213 d->pushEvent("===== key for T pointed right =====");
00214 d->displayText("Press key for response for T pointed right:");
00215 keyright = d->waitForKey();
00216 if (keyleft == keyright) {
00217 d->displayText(sformat("You must choose two different keys (%d/%d).",keyleft,keyright));
00218 d->waitForKey();
00219 }
00220 }
00221 while (keyleft == keyright);
00222
00223
00224
00225
00226 if (etc->getModelParamString("EyeTrackerType").compare("EL") == 0) {
00227 d->pushEventBegin("Calibration");
00228 et->setBackgroundColor(d);
00229 et->calibrate(d);
00230 d->pushEventEnd("Calibration");
00231 }
00232 else if(etc->getModelParamString("EyeTrackerType").compare("ISCAN") == 0) {
00233 d->clearScreen();
00234 d->displayISCANcalib();
00235 d->waitForKey();
00236
00237
00238 d->displayText("Press any key to calibrate; other key to skip");
00239 int c = d->waitForKey();
00240 if (c == ' ') d->displayEyeTrackerCalibration(3, 3);
00241 }
00242
00243
00244 d->clearScreen();
00245 d->displayText("Press any key to start the experiment");
00246 d->waitForKey();
00247
00248
00249
00250
00251 const uint Nrepeats = 6;
00252 const uint Nblocks = fromStr<int>(argv[1]);;
00253 const uint Ntrials = 12;
00254 const uint Nbreak = 5;
00255
00256
00257 const double Ntimeout = 5.0;
00258
00259
00260 const uint Nnoises = 256;
00261
00262
00263 const int itemsize = 90;
00264 const int setSize = fromStr<int>(argv[2]);
00265 const double grid_spacing = 120.0, min_spacing = grid_spacing;
00266
00267
00268 const Dims screenDims =
00269 fromStr<Dims>(manager.getOptionValString(&OPT_SDLdisplayDims));
00270
00271
00272 const std::string Ttype = "T", Dtype = "L";
00273 const double phiMax = M_PI*5/8, phiMin = M_PI*3/8;
00274
00275 ClassicSearchItemFactory
00276 targetsLeft(SearchItem::FOREGROUND, Ttype,
00277 itemsize,
00278 Range<double>(-phiMax,-phiMin)),
00279 targetsRight(SearchItem::FOREGROUND, Ttype,
00280 itemsize,
00281 Range<double>(phiMin,phiMax)),
00282 distractors(SearchItem::BACKGROUND, Dtype,
00283 itemsize,
00284 Range<double>(-M_PI/2,M_PI/2));
00285
00286
00287 std::vector<rutz::shared_ptr<trialAgenda> > trials;
00288 int tIndex[Ntrials];
00289 NoiseColor colors[Ntrials];
00290 bool rep[Ntrials];
00291
00292 SearchArray sarray(screenDims, grid_spacing, min_spacing, itemsize);
00293 const PixRGB<byte> gray(128,128,128);
00294
00295
00296 initRandomNumbers();
00297 for (uint i = 0; i < Ntrials; i++)
00298 {
00299 tIndex[i] = i;
00300 colors[i] = NoiseColor(i%3);
00301 rep[i] = (i < Nrepeats);
00302 }
00303
00304 for (uint i = 0; i < Ntrials; i++)
00305 {
00306
00307 const geom::vec2d pos = randomPtIn(sarray.itemBounds());
00308
00309 rutz::shared_ptr<trialAgenda> myTrial(new trialAgenda(rep[i],colors[i],pos,screenDims));
00310 myTrial->randomizeNoise(Nnoises);
00311 trials.push_back(myTrial);
00312 }
00313
00314 char buf[256];
00315
00316 for (uint iblock = 1; iblock <= Nblocks; iblock++) {
00317
00318
00319 randShuffle(tIndex,Ntrials);
00320
00321 d->displayText(sformat("Beginning block %d",iblock));
00322 d->waitForKey();
00323
00324 sprintf(buf,"===== Beginning block %u/%u =====", iblock,Nblocks);
00325 d->pushEvent(buf);
00326 LINFO("%s",buf);
00327
00328 rutz::shared_ptr<trialAgenda> thisTrial;
00329
00330 for (uint itrial = 1; itrial <= Ntrials; itrial++) {
00331
00332
00333 sprintf(buf, "===== Block %u/%u, Trial %u/%u =====",
00334 iblock, Nblocks, itrial, Ntrials);
00335 d->pushEvent(buf);
00336 LINFO("%s",buf);
00337
00338
00339 thisTrial = trials[tIndex[itrial-1]];
00340
00341
00342 d->clearScreen();
00343
00344
00345 if(!(thisTrial->repeated))
00346 thisTrial->randomizeNoise(Nnoises);
00347
00348
00349 Image< PixRGB<byte> > bkgdimg = thisTrial->generateBkgd();
00350
00351
00352 sarray.clear();
00353
00354
00355
00356
00357 geom::vec2d P = thisTrial->targetloc;
00358 const bool isTargetLeft = (randomDouble() < 0.5);
00359 const std::string tarDir = isTargetLeft ? "left" : "right";
00360 if (isTargetLeft)
00361 sarray.addElement(targetsLeft.make(P));
00362 else
00363 sarray.addElement(targetsRight.make(P));
00364
00365
00366 sarray.generateBackground(distractors, 2, false, 5000, false);
00367
00368 sarray.pareBackground(setSize);
00369
00370
00371 Image< PixRGB<byte> > img = bkgdimg + sarray.getImage() - gray;
00372 SDL_Surface *surf1 = d->makeBlittableSurface(img, true);
00373
00374
00375 usleep(200000);
00376
00377
00378 if (etc->getModelParamString("EyeTrackerType").compare("EL") == 0) {
00379 et->recalibrate(d,-1);
00380
00381 d->displayFixation();
00382 usleep(300000);
00383 et->track(true);
00384 d->displayFixationBlink(-1,-1,2,2);
00385 }
00386 else if(etc->getModelParamString("EyeTrackerType").compare("ISCAN") == 0) {
00387
00388 d->displayFixation();
00389 d->waitForKey();
00390
00391 et->track(true);
00392 d->displayFixationBlink(-1,-1,2,2);
00393 }
00394 else {
00395 d->displayFixation();
00396 usleep(100000);
00397 d->displayFixationBlink(-1,-1,2,2);
00398 }
00399
00400
00401 sprintf(buf, "===== Showing array: %s, turned %s =====",
00402 toStr(*thisTrial).c_str(), tarDir.c_str());
00403 LINFO("%s",buf);
00404
00405
00406 d->waitNextRequestedVsync(false, true);
00407 d->pushEvent(buf);
00408 d->displaySurface(surf1, 0, true);
00409
00410
00411 const int resp = d->waitForKeyTimeout(1000*Ntimeout);
00412
00413
00414 if(resp == -1)
00415 sprintf(buf, "trial timed out");
00416 else if(resp != keyleft && resp != keyright)
00417 sprintf(buf,"trial mistaken - neither key pressed (%c, %c/%c)",resp, keyleft, keyright);
00418 else if((resp==keyleft) == isTargetLeft)
00419 sprintf(buf,"trial correct, correct = %s", tarDir.c_str());
00420 else
00421 sprintf(buf,"trial incorrect, correct = %s", tarDir.c_str());
00422
00423
00424 d->pushEvent(buf);
00425 LINFO("%s",buf);
00426
00427 usleep(50000);
00428 et->track(false);
00429
00430
00431 d->clearScreen();
00432 SDL_FreeSurface(surf1);
00433 }
00434
00435
00436 if (iblock%Nbreak==0 && iblock!=Nblocks)
00437 {
00438 d->displayText("Please take a break: press any key to continue when ready.");
00439 d->waitForKey();
00440
00441 if (etc->getModelParamString("EyeTrackerType").compare("EL") == 0) {
00442 d->pushEventBegin("Calibration");
00443 et->setBackgroundColor(d);
00444 et->calibrate(d);
00445 d->pushEventEnd("Calibration");
00446 }
00447 else if(etc->getModelParamString("EyeTrackerType").compare("ISCAN") == 0) {
00448 d->clearScreen();
00449 d->displayISCANcalib();
00450 d->waitForKey();
00451
00452
00453 d->displayText("<SPACE> to calibrate; other key to skip");
00454 int c = d->waitForKey();
00455 if (c == ' ') d->displayEyeTrackerCalibration(3, 3);
00456 }
00457 }
00458 }
00459
00460 d->clearScreen();
00461 d->displayText("Search trials complete.");
00462 d->waitForKey();
00463
00464 d->displayText("Which background is more familiar?");
00465 d->waitForKey();
00466
00467
00468 int iRepeat = 0;
00469 for(uint i = 0; i < Ntrials; i++) {
00470 rutz::shared_ptr<trialAgenda> thisTrial = trials[i];
00471 if(!thisTrial->repeated) continue;
00472 iRepeat++;
00473
00474
00475 trialAgenda randTrial = (*thisTrial);
00476 Dims halfDims = screenDims/2;
00477
00478
00479
00480 Image<PixRGB<byte> > rpt_bkgd = rescale(thisTrial->generateBkgd(),halfDims);
00481 randTrial.randomizeNoise(Nnoises);
00482 Image<PixRGB<byte> > rand_bkgd = rescale(randTrial.generateBkgd(),halfDims);
00483
00484
00485 const bool isRepeatLeft = (randomDouble() < 0.5);
00486 const std::string repeatDir = isRepeatLeft ? "left" : "right";
00487
00488 Image<PixRGB<byte> > two_bkgd;
00489 if (isRepeatLeft)
00490 two_bkgd = concatX(rpt_bkgd, rand_bkgd);
00491 else
00492 two_bkgd = concatX(rand_bkgd,rpt_bkgd);
00493
00494
00495 drawLine(two_bkgd, Point2D<int>(halfDims.w()-1, 0), Point2D<int>(halfDims.w()-1, halfDims.h()-1),
00496 PixRGB<byte>(255, 255, 0), 1);
00497 drawLine(two_bkgd, Point2D<int>(halfDims.w(), 0), Point2D<int>(halfDims.w(), halfDims.h()-1),
00498 PixRGB<byte>(255, 255, 0), 1);
00499
00500 sprintf(buf, "===== Recognition for array: %s, on %s side =====",
00501 toStr(*thisTrial).c_str(), repeatDir.c_str());
00502 LINFO("%s",buf);
00503
00504 d->displayText(sformat("Recognition trial %d",iRepeat));
00505 d->waitForKey();
00506 SDL_Surface *surf1 = d->makeBlittableSurface(two_bkgd, true);
00507
00508
00509 d->waitNextRequestedVsync(false, true);
00510 d->pushEvent(buf);
00511 d->displaySurface(surf1, 0, true);
00512
00513
00514 const int resp = d->waitForKey();
00515
00516
00517 if(resp != keyleft && resp != keyright)
00518 sprintf(buf,"recognition trial mistaken - neither key pressed (%c, %c/%c)",resp, keyleft, keyright);
00519 else if((resp==keyleft) == isRepeatLeft)
00520 sprintf(buf,"recognition trial correct, correct = %s", repeatDir.c_str());
00521 else
00522 sprintf(buf,"recognition trial incorrect, correct = %s", repeatDir.c_str());
00523
00524
00525 d->pushEvent(buf);
00526 LINFO("%s",buf);
00527 }
00528
00529
00530 d->displayText("Where would the target be? Click with the mouse.");
00531 d->waitForMouseClick();
00532
00533 iRepeat = 0;
00534 for(uint i = 0; i < Ntrials; i++) {
00535 rutz::shared_ptr<trialAgenda> thisTrial = trials[i];
00536 if(!thisTrial->repeated) continue;
00537 iRepeat++;
00538
00539 Image<PixRGB<byte> > rpt_bkgd = thisTrial->generateBkgd();
00540
00541
00542 sprintf(buf, "===== Target retrieval for array: %s =====",
00543 toStr(*thisTrial).c_str());
00544 LINFO("%s",buf);
00545
00546 d->displayText(sformat("Target retrieval trial %d",iRepeat));
00547 d->waitForMouseClick();
00548 SDL_Surface *surf1 = d->makeBlittableSurface(rpt_bkgd, true);
00549
00550
00551 d->waitNextRequestedVsync(false, true);
00552 d->pushEvent(buf);
00553 d->displaySurface(surf1, 0, true);
00554
00555
00556 d->showCursor(true);
00557 SDL_Event event;
00558 while( SDL_WaitEvent( &event )) {
00559 if(event.type == SDL_MOUSEBUTTONDOWN && event.button.button == SDL_BUTTON_LEFT ) {
00560 break;
00561 }
00562 }
00563
00564
00565
00566
00567
00568
00569
00570
00571
00572
00573
00574
00575
00576
00577
00578
00579
00580
00581 d->showCursor(false);
00582
00583 Point2D<int> resp(event.button.x,event.button.y);
00584
00585 sprintf(buf,"target placed at %s",toStr(resp).c_str());
00586
00587
00588 d->pushEvent(buf);
00589 LINFO("%s",buf);
00590 }
00591
00592 d->displayText("Experiment complete. Press any key to exit. Thank you!");
00593 d->waitForKey();
00594
00595
00596 manager.stop();
00597
00598
00599 return 0;
00600 }
00601
00602
00603
00604 extern "C" int main(const int argc, char** argv)
00605 {
00606
00607
00608
00609
00610 try
00611 {
00612 return submain(argc, argv);
00613 }
00614 catch (...)
00615 {
00616 REPORT_CURRENT_EXCEPTION;
00617 }
00618
00619 return 1;
00620 }
00621
00622
00623
00624
00625