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 #include "Component/ModelManager.H"
00039 #include "Image/CutPaste.H"
00040 #include "Image/DrawOps.H"
00041 #include "Image/FilterOps.H"
00042 #include "Image/Image.H"
00043 #include "Image/Kernels.H"
00044 #include "Psycho/PsychoDisplay.H"
00045 #include "GUI/GUIOpts.H"
00046 #include "Psycho/Staircase.H"
00047 #include "Raster/Raster.H"
00048 #include "Util/MathFunctions.H"
00049 #include "Util/Timer.H"
00050
00051 #include <deque>
00052 #include <fstream>
00053
00054 #define GABORSD 15.0F
00055 #define GABORPER 15.0F
00056
00057
00058 #define NFSETUP 12*60
00059
00060
00061 #define NFINIT 36
00062
00063
00064 #define NFSTIM 37
00065
00066
00067 #define NFIPI 15
00068
00069
00070 #define NFISI 55
00071
00072
00073 #define NTRIAL 12
00074
00075
00076
00077
00078
00079
00080
00081
00082
00083
00084
00085 const bool blocking = false;
00086
00087
00088 bool shadow = true;
00089
00090
00091 enum TaskType { None = 0, Orientation = 1, Drift = 2, Blank = 3 };
00092
00093
00094 struct PsychoTask {
00095 nub::soft_ref<Staircase> s;
00096 TaskType tt;
00097 float ampl;
00098 double theta;
00099 double drift;
00100 };
00101
00102
00103 void parseTaskDefinition(const std::string& arg, PsychoTask& p,
00104 const std::string name)
00105 {
00106 if (arg.length() != 3) LFATAL("Task definition should have 3 chars");
00107 p.s->stop();
00108 p.s->setModelParamVal("FileName", name);
00109 switch(arg[0])
00110 {
00111 case 'N':
00112 p.tt = None;
00113 p.ampl = 127.0f;
00114 p.s->setModelParamVal("InitialValue", 0.0);
00115 p.s->setModelParamVal("DeltaValue", 0.0);
00116 p.s->setModelParamVal("MinValue", 0.0);
00117 p.s->setModelParamVal("MaxValue", 0.0);
00118 break;
00119 case 'O':
00120 p.tt = Orientation;
00121 p.ampl = 127.0f;
00122 p.s->setModelParamVal("InitialValue", 4.0);
00123 p.s->setModelParamVal("DeltaValue", 0.5);
00124 p.s->setModelParamVal("MinValue", 4.0);
00125 p.s->setModelParamVal("MaxValue", 4.0);
00126 break;
00127 case 'D':
00128 p.tt = Drift;
00129 p.ampl = 127.0f;
00130 p.s->setModelParamVal("InitialValue", 8.0);
00131 p.s->setModelParamVal("DeltaValue", 2.0);
00132 p.s->setModelParamVal("MinValue", 8.0);
00133 p.s->setModelParamVal("MaxValue", 8.0);
00134 break;
00135 case 'B':
00136 p.tt = Blank;
00137 p.ampl = 0.0f;
00138 p.s->setModelParamVal("InitialValue", 0.0);
00139 p.s->setModelParamVal("DeltaValue", 0.0);
00140 p.s->setModelParamVal("MinValue", 0.0);
00141 p.s->setModelParamVal("MaxValue", 0.0);
00142 break;
00143 default:
00144 LFATAL("Incorrect task '%c'", arg[0]);
00145 }
00146
00147 switch(arg[1])
00148 {
00149 case 'V': p.theta = 0.0; break;
00150 case 'H': p.theta = 90.0; break;
00151 default: LFATAL("Incorrect orientation definition '%c'", arg[1]);
00152 }
00153
00154 switch(arg[2])
00155 {
00156 case 'F': p.drift = 28.0; break;
00157 case 'S': p.drift = 16.0; break;
00158 default: LFATAL("Incorrect drift definition '%c'", arg[1]);
00159 }
00160 p.s->start();
00161 }
00162
00163
00164
00165
00166 extern "C" int main(const int argc, char** argv)
00167 {
00168 MYLOGVERB = LOG_INFO;
00169
00170
00171 ModelManager manager("Psycho OBFBA");
00172
00173
00174 nub::soft_ref<PsychoDisplay> d(new PsychoDisplay(manager));
00175 manager.addSubComponent(d);
00176
00177 manager.setOptionValString(&OPT_SDLdisplayDims, "640x480");
00178 manager.setOptionValString(&OPT_SDLdisplayRefreshUsec, "16666.66666");
00179
00180 PsychoTask p[2];
00181 p[0].s.reset(new Staircase(manager, "obfbaL", "obfbaL"));
00182 manager.addSubComponent(p[0].s);
00183 p[1].s.reset(new Staircase(manager, "obfbaR", "obfbaR"));
00184 manager.addSubComponent(p[1].s);
00185
00186 Image< PixRGB<byte> > background = Raster::ReadRGB("b640.ppm");
00187
00188
00189 if (manager.parseCommandLine(argc, argv, "<taskfile.txt>", 1, 1) == false)
00190 return(1);
00191
00192
00193 manager.start();
00194
00195
00196 try {
00197
00198 std::string fname = manager.getExtraArg(0);
00199 std::ifstream ifs(fname.c_str());
00200 if (ifs.is_open() == false)
00201 LFATAL("Cannot read %s", manager.getExtraArg(0).c_str());
00202 std::deque<std::string> line;
00203 while(1) {
00204 char str[1000];
00205 ifs.getline(str, 1000);
00206 if (strlen(str) == 0) break;
00207 line.push_back(std::string(str));
00208 }
00209 ifs.close();
00210
00211
00212 int w = d->getDims().w(), h = d->getDims().h();
00213 PixRGB<byte> black(0), grey(d->getGrey()), white(255);
00214
00215 Image< PixRGB<byte> > background2 = background;
00216 Image< PixRGB<byte> > blk(w-80, h-120, NO_INIT);
00217 blk.clear(PixRGB<byte>(grey));
00218 inplacePaste(background, blk, Point2D<int>(40, 60));
00219
00220
00221 drawCross(background, Point2D<int>(w/2, h/2), black, 10, 2);
00222 drawCross(background, Point2D<int>(w/2, h/2), white, 10, 1);
00223 drawCross(background2, Point2D<int>(w/2, h/2), black, 10, 2);
00224 drawCross(background2, Point2D<int>(w/2, h/2), white, 10, 1);
00225
00226
00227 Image<float> tmp = gaborFilter<float>(GABORSD, GABORPER, 0.0F, 0.0F);
00228 int pw = tmp.getWidth(), ph = tmp.getHeight();
00229 LINFO("Gabor patch size: %dx%d", pw, ph);
00230 Point2D<int> lpos((w/2 - pw) / 2, (h - ph) / 2);
00231 Point2D<int> rpos(w/2 + (w/2 - pw) / 2, (h - ph) / 2);
00232 Image< PixRGB<byte> > blank(tmp.getDims(), NO_INIT);
00233 blank.clear(PixRGB<byte>(grey));
00234
00235
00236 int border = 40;
00237 Image<float> mult(background2.getDims(), NO_INIT);
00238 mult.clear(1.0F);
00239 Image<float> mask(blank.getWidth() + border * 2,
00240 blank.getHeight() + border * 2, NO_INIT);
00241 mask.clear(0.5F);
00242 int off = 10;
00243 inplacePaste(mult, mask, Point2D<int>(lpos.i-border+off, lpos.j-border+off));
00244 inplacePaste(mult, mask, Point2D<int>(rpos.i-border+off, rpos.j-border+off));
00245 background2 *= mult;
00246 Image< PixRGB<byte> > tmp2(blank.getWidth() + border*2,
00247 blank.getHeight() + border * 2, NO_INIT);
00248 tmp2.clear(PixRGB<byte>(grey));
00249 inplacePaste(background2, tmp2, Point2D<int>(lpos.i - border, lpos.j - border));
00250 inplacePaste(background2, tmp2, Point2D<int>(rpos.i - border, rpos.j - border));
00251
00252
00253 SDL_Surface *bgimg = d->makeBlittableSurface(background);
00254 SDL_Surface *bg2img = d->makeBlittableSurface(background2);
00255
00256
00257 Timer tim, tim2, tim3;
00258 if (system("/bin/sync")) LERROR("error in sync");
00259 usleep(500000);
00260
00261
00262 d->displayText("Ready");
00263 while(d->waitForKey() != ' ') ;
00264 tim.reset();
00265 d->clearScreen(true);
00266 d->waitFrames(NFSETUP);
00267 LINFO("Scanner warmup took %llxms", tim.get());
00268 int count = 0;
00269
00270 std::string msg; bool do_wait = false;
00271 while(line.size() > 0)
00272 {
00273 std::string str = line.front(); line.pop_front();
00274
00275
00276 if (str[0] == '#')
00277 {
00278 msg = str; msg.erase(0, 1); do_wait = false;
00279 if (msg[0] == '#') { msg.erase(0, 1); do_wait = true; }
00280 }
00281 else
00282
00283 {
00284 tim3.reset();
00285
00286 for (int i = 0; i < 2; i ++) {
00287 std::string xx(str.c_str() + i*4, 3);
00288 std::string pname("STAIR-");
00289 if (i == 0) pname += "L"; else pname += "R";
00290 char gogo[10]; sprintf(gogo, "%03d", count);
00291 pname += gogo; pname += fname;
00292 parseTaskDefinition(xx, p[i], pname);
00293 }
00294 count ++;
00295
00296
00297 float sd = GABORSD, per = GABORPER;
00298 Image< PixRGB<byte> > left, right;
00299
00300
00301 if (do_wait) d->waitForKey();
00302
00303
00304 LINFO("%s", msg.c_str());
00305 d->displayText(msg.c_str());
00306 d->waitFrames(NFINIT);
00307 d->waitNextRequestedVsync(false, true);
00308
00309
00310 if (str.length() > 8)
00311 d->displaySurface(bg2img, -2);
00312 else
00313 d->displaySurface(bgimg, -2);
00314
00315
00316 for (int trial = 0; trial < NTRIAL; trial ++)
00317 {
00318 tim.reset();
00319 tim2.reset();
00320
00321 int nf = NFSTIM;
00322 int nb1 = NFIPI;
00323 int nb2 = NFISI;
00324 float base = d->getGrey().luminance();
00325 int rphil = randomUpToIncluding(90);
00326 int rphir = randomUpToIncluding(90);
00327
00328 int idxL = 0, idxR = 1;
00329 double dl1, dl2, dr1, dr2;
00330 p[idxL].s->getValues(dl1, dl2);
00331 p[idxR].s->getValues(dr1, dr2);
00332
00333 double dt1L = 0.0, dt2L = 0.0, dd1L = 0.0, dd2L = 0.0,
00334 dt1R = 0.0, dt2R = 0.0, dd1R = 0.0, dd2R = 0.0;
00335 switch(p[idxL].tt)
00336 {
00337 case None:
00338 case Blank:
00339 dt1L = 0.0; dt2L = 0.0; dd1L = 0.0; dd2L = 0.0;
00340 break;
00341 case Orientation:
00342 dt1L = dl1; dt2L = dl2; dd1L = 0.0; dd2L = 0.0;
00343 break;
00344 case Drift:
00345 dt1L = 0.0; dt2L = 0.0; dd1L = dl1; dd2L = dl2;
00346 break;
00347 }
00348 switch(p[idxR].tt)
00349 {
00350 case None:
00351 case Blank:
00352 dt1R = 0.0; dt2R = 0.0; dd1R = 0.0; dd2R = 0.0;
00353 break;
00354 case Orientation:
00355 dt1R = dr1; dt2R = dr2; dd1R = 0.0; dd2R = 0.0;
00356 break;
00357 case Drift:
00358 dt1R = 0.0; dt2R = 0.0; dd1R = dr1; dd2R = dr2;
00359 break;
00360 }
00361
00362
00363 for (int i = 0; i < nf; i ++)
00364 {
00365 left =
00366 gaborFilter<byte>(sd, per,
00367 i*(p[idxL].drift + dd1L) + rphil,
00368 p[idxL].theta + dt1L, base,
00369 p[idxL].ampl);
00370 right =
00371 gaborFilter<byte>(sd, per,
00372 i*(p[idxR].drift + dd1R) + rphir,
00373 p[idxR].theta + dt1R, base,
00374 p[idxR].ampl);
00375 d->displayImagePatch(left, lpos, i, false, false);
00376 d->displayImagePatch(right, rpos, i, true, true);
00377 }
00378 long int t0 = tim.getReset();
00379
00380
00381 d->displayImagePatch(blank, lpos, -2, false, false);
00382 d->displayImagePatch(blank, rpos, -2);
00383 d->waitFrames(nb1);
00384 long int t1 = tim.getReset();
00385
00386
00387 rphil = randomUpToIncluding(90);
00388 rphir = randomUpToIncluding(90);
00389 for (int i = 0; i < nf; i ++)
00390 {
00391 left =
00392 gaborFilter<byte>(sd, per,
00393 i*(p[idxL].drift + dd2L) + rphil,
00394 p[idxL].theta + dt2L, base,
00395 p[idxL].ampl);
00396 right =
00397 gaborFilter<byte>(sd, per,
00398 i*(p[idxR].drift + dd2R) + rphir,
00399 p[idxR].theta + dt2R, base,
00400 p[idxR].ampl);
00401 d->displayImagePatch(left, lpos, i, false, false);
00402 d->displayImagePatch(right, rpos, i, true, true);
00403 }
00404 long int t2 = tim.getReset();
00405
00406
00407 d->displayImagePatch(blank, lpos, -2, false, false);
00408 d->displayImagePatch(blank, rpos, -2);
00409 if (blocking == false) d->waitFrames(nb2);
00410
00411
00412 if (p[idxL].tt != None && p[idxL].tt != Blank)
00413 {
00414 int c;
00415 if (blocking) c = d->waitForKey();
00416 else c = d->checkForKey();
00417 if (c != -1) p[idxL].s->setResponse( (c == '\r') );
00418 else p[idxL].s->setResponse( (randomDouble() < 0.5) );
00419 }
00420 else p[idxL].s->setResponse(false);
00421
00422 if (p[idxR].tt != None && p[idxR].tt != Blank)
00423 {
00424 int c;
00425 if (blocking) c = d->waitForKey();
00426 else c = d->checkForKey();
00427 if (c != -1) p[idxR].s->setResponse( (c == '\r') );
00428 else p[idxR].s->setResponse( (randomDouble() < 0.5) );
00429 }
00430 else p[idxR].s->setResponse(false);
00431
00432 long int t3 = tim.getReset();
00433 long int tt = tim2.get();
00434 float pe = 16.666666f;
00435 LINFO("Trial %d: p1=%ldms (%df) ipi=%ldms (%df) p2=%ldms (%df) "
00436 "isi=%ldms (%df) tot=%ldms (%df)",
00437 trial,
00438 t0, int(t0/pe + 0.4999f),
00439 t1, int(t1/pe + 0.4999f),
00440 t2, int(t2/pe + 0.4999f),
00441 t3, int(t3/pe + 0.4999f),
00442 tt, int(tt/pe + 0.4999f));
00443 }
00444
00445
00446 for (int i = 0; i < 2; ++i)
00447 p[i].s->reset(MC_RECURSE);
00448
00449 float pe = 16.666666f;
00450 long int tt = tim3.get();
00451 LINFO("Task took %ldms (%df)", tt, int(tt/pe + 0.4999f));
00452 }
00453 }
00454
00455 d->clearScreen();
00456 d->displayText("Experiment complete. Thank you!");
00457 d->waitFrames(100);
00458 }
00459 catch (...)
00460 {
00461 REPORT_CURRENT_EXCEPTION;
00462 }
00463
00464
00465 manager.stop();
00466
00467
00468 return 0;
00469 }
00470
00471
00472
00473
00474
00475