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 "Neuro/EyeHeadControllers.H"
00039 #include "Neuro/SaccadeControllers.H"
00040 #include "Neuro/NeuroOpts.H"
00041 #include "Neuro/NeuroSimEvents.H"
00042 #include "Neuro/SpatialMetrics.H"
00043 #include "Component/OptionManager.H"
00044 #include "Psycho/EyeTrace.H"
00045 #include "Simulation/SimEventQueue.H"
00046 #include "Util/StringConversions.H"
00047 #include "Util/StringUtil.H"
00048 #include "Util/sformat.H"
00049
00050 #include <vector>
00051
00052
00053
00054
00055 StubEyeHeadController::StubEyeHeadController(OptionManager& mgr) :
00056 EyeHeadController(mgr, "Stub Eye/Head Controller",
00057 "StubEyeHeadController")
00058 { }
00059
00060
00061 StubEyeHeadController::~StubEyeHeadController()
00062 { }
00063
00064
00065
00066
00067 SimpleEyeHeadController::SimpleEyeHeadController(OptionManager& mgr) :
00068 EyeHeadController(mgr, "Simple Eye/Head Controller",
00069 "SimpleEyeHeadController"),
00070 SIMCALLBACK_INIT(SimEventClockTick),
00071 itsSCCeye(new SaccadeControllerEyeConfigurator(mgr)),
00072 itsSCChead(new SaccadeControllerHeadConfigurator(mgr)),
00073 itsSCeye(), itsSChead()
00074 {
00075 addSubComponent(itsSCCeye);
00076 addSubComponent(itsSCChead);
00077 }
00078
00079
00080 SimpleEyeHeadController::~SimpleEyeHeadController()
00081 { }
00082
00083
00084 void SimpleEyeHeadController::start1()
00085 {
00086
00087 itsSCeye = itsSCCeye->getSC();
00088 itsSChead = itsSCChead->getSC();
00089
00090 if (itsSCeye.is_invalid() || itsSChead.is_invalid())
00091 LFATAL("I need valid eye and head SaccadeControllers");
00092
00093 EyeHeadController::start1();
00094 }
00095
00096
00097 void SimpleEyeHeadController::
00098 onSimEventClockTick(SimEventQueue& q, rutz::shared_ptr<SimEventClockTick>& ect)
00099 {
00100
00101 if (SeC<SimEventWTAwinner> e = q.check<SimEventWTAwinner>(this))
00102 itsSCeye->setPercept(e->winner(), q);
00103
00104
00105 if (SeC<SimEventSaccadeStatusEye> e = q.check<SimEventSaccadeStatusEye>(this))
00106 {
00107 WTAwinner win(e->position(), q.now(), 0.0, false);
00108 itsSChead->setPercept(win, q);
00109 }
00110
00111
00112
00113 itsSCeye->evolve(q);
00114 itsSChead->evolve(q);
00115
00116
00117
00118
00119 itsSCeye->getDecision(q, true);
00120 itsSChead->getDecision(q, true);
00121 }
00122
00123
00124
00125
00126 EyeTrackerEyeHeadController::EyeTrackerEyeHeadController(OptionManager& mgr) :
00127 EyeHeadController(mgr, "EyeTracker Eye/Head Controller",
00128 "EyeTrackerEyeHeadController"),
00129 SIMCALLBACK_INIT(SimEventClockTick),
00130 itsConfig(&OPT_EHCeyeTrackConfig, this),
00131 itsEyeTrace(), itsEyeSample()
00132 { }
00133
00134
00135 EyeTrackerEyeHeadController::~EyeTrackerEyeHeadController()
00136 { }
00137
00138
00139 void EyeTrackerEyeHeadController::start1()
00140 {
00141
00142 std::vector<std::string> tok;
00143 split(itsConfig.getVal(), ",", std::back_inserter(tok));
00144 if (tok.empty()) LFATAL("I cannot run without at least one eyetrace.");
00145
00146 for (uint i = 0; i < tok.size(); i ++)
00147 {
00148 std::vector<std::string> tt;
00149 split(tok[i], ":", std::back_inserter(tt));
00150 if (tt.empty()) LFATAL("Invalid empty eye-tracker filename");
00151
00152 std::string fname = tt[0];
00153 std::string extras = join(tt.begin() + 1, tt.end(), ":");
00154
00155 LINFO("Instantiating Tracker %03d with file '%s', extras '%s'",
00156 i, fname.c_str(), extras.c_str());
00157
00158
00159 PixRGB<byte> color(128, 255, 255);
00160 if (tt.size() > 1) convertFromString(tt[1], color);
00161
00162
00163 rutz::shared_ptr<EyeTrace> et(new EyeTrace(fname, color));
00164
00165 itsEyeTrace.push_back(et);
00166 itsEyeSample.push_back(0);
00167 }
00168
00169 EyeHeadController::start1();
00170 }
00171
00172
00173 void EyeTrackerEyeHeadController::
00174 onSimEventClockTick(SimEventQueue& q, rutz::shared_ptr<SimEventClockTick>& ect)
00175 {
00176 const SimTime t = q.now();
00177
00178
00179 bool keepgoing = true;
00180 while(keepgoing)
00181 {
00182 keepgoing = false;
00183
00184
00185 for (uint i = 0; i < itsEyeTrace.size(); i ++)
00186 if (itsEyeTrace[i]->hasData(itsEyeSample[i], t))
00187 {
00188
00189 keepgoing = true;
00190
00191
00192 rutz::shared_ptr<EyeData> data = itsEyeTrace[i]->data(itsEyeSample[i]);
00193
00194 CLDEBUG("Human eye %03u [%07"ZU"] (%d, %d) at %.1fms",
00195 i, itsEyeSample[i], data->position().i, data->position().j,
00196 itsEyeSample[i] * itsEyeTrace[i]->period().msecs());
00197
00198
00199 q.post(rutz::make_shared(new SimEventEyeTrackerData(this, data, i, itsEyeTrace[i]->filename(), itsEyeTrace[i]->color(), itsEyeTrace[i]->ppd(),itsEyeTrace[i]->period())));
00200
00201
00202 SaccadeState prevSacState = SACSTATE_UNK;
00203 bool prevBlinkState = false;
00204 if (itsEyeSample[i] > 0) {
00205 rutz::shared_ptr<EyeData> prevEyeData = itsEyeTrace[i]->data(itsEyeSample[i] - 1);
00206 prevSacState = prevEyeData->saccadeState();
00207 prevBlinkState = prevEyeData->isInBlink();
00208 }
00209
00210
00211 Point2D<int> retinaleyepos = data->position();
00212 if (SeC<SimEventRetinaImage> e = q.check<SimEventRetinaImage>(this, SEQ_ANY))
00213 retinaleyepos = e->rawToRetinal(retinaleyepos);
00214
00215 q.post(rutz::make_shared(new SimEventSaccadeStatusEye(this, retinaleyepos, data->saccadeState(),
00216 prevSacState, data->isInBlink(), prevBlinkState)));
00217
00218
00219 ++ itsEyeSample[i];
00220 }
00221 }
00222 }
00223
00224
00225
00226
00227 MonkeyEyeHeadController::
00228 MonkeyEyeHeadController(OptionManager& mgr) :
00229 EyeHeadController(mgr, "Monkey Eye/Head Controller", "MonkeyEyeHeadController"),
00230 SIMCALLBACK_INIT(SimEventClockTick),
00231 itsBlinkWait(&OPT_SCeyeBlinkWaitTime, this),
00232 itsBlinkDur(&OPT_SCeyeBlinkDuration, this),
00233 itsOdist(&OPT_SCeyeThreshMinOvert, this),
00234 itsMetrics(new SpatialMetrics(mgr)),
00235 itsTSC(new ThresholdSaccadeController(mgr, SaccadeBodyPartEye)),
00236 lastsbt(), blinkt()
00237 {
00238 this->addSubComponent(itsMetrics);
00239
00240
00241
00242
00243
00244 this->addSubComponent(itsTSC);
00245
00246 lastsbt = SimTime::ZERO(); blinkt = SimTime::ZERO();
00247 }
00248
00249
00250 MonkeyEyeHeadController::~MonkeyEyeHeadController()
00251 { }
00252
00253
00254 void MonkeyEyeHeadController::
00255 onSimEventClockTick(SimEventQueue& q, rutz::shared_ptr<SimEventClockTick>& ect)
00256 {
00257
00258
00259
00260
00261
00262
00263
00264
00265
00266
00267
00268
00269
00270
00271
00272
00273
00274
00275
00276
00277
00278
00279
00280
00281
00282
00283
00284
00285
00286
00287
00288
00289
00290
00291
00292 }
00293
00294 // do we want to blink?
00295 if (itsSCeye->getBlinkState() == false && itsSCeye->isInSaccade() == false &&
00296 t - lastsbt > itsBlinkWait.getVal())
00297 {
00298 // check whether our percepts are nicely clustered (no action going on)
00299 bool areclose; Point2D<int> avgp;
00300 itsTSC->checkPercepts(areclose, avgp);
00301 if (areclose)
00302 {
00303 // start a blink with very small probability, or with a higher
00304 // probability if the percepts are clustered, or for sure if it
00305 // has been a long time even though the percepts may not be
00306 // clustered:
00307 double r = randomDouble();
00308 if (r > 0.99995 ||
00309 (r > 0.9999 && areclose == true) ||
00310 (r > 0.999 && t - lastsbt > itsBlinkWait.getVal() * 2.0) ||
00311 (r > 0.9 && t - lastsbt > itsBlinkWait.getVal() * 3.0))
00312 {
00313 LINFO("===== Starting blink at t=%.2fms (last at %.2fms)=====",
00314 t.msecs(), lastsbt.msecs());
00315
00316 }
00317 }
00318 }
00319 */
00320 }
00321
00322
00323 void MonkeyEyeHeadController::setPercept(const WTAwinner& fix,
00324 SimEventQueue& q)
00325 {
00326
00327 itsTSC->setPercept(fix, q);
00328
00329
00330
00331
00332
00333
00334
00335
00336
00337
00338
00339
00340
00341
00342
00343
00344
00345
00346
00347
00348
00349
00350
00351
00352
00353
00354
00355
00356
00357
00358
00359
00360
00361
00362
00363
00364 Point2D<int> htarget;
00365 itsMetrics->deg2pix(hampx + headcx, hampy + headcy, htarget);
00366
00367 }
00368 else
00369 {
00370 LINFO("=== Smooth pursuit to (%d, %d) at %.2fms ===",
00371 target.i, target.j, fix.t.msecs());
00372
00373 // we have a recommendation from itsTSC, but it's too small for
00374 // a realistic saccade. Just use it as friction targets:
00375 WTAwinner win(fix); // new target for our eye controller
00376 win.p = target; // use target location from itsTSC as percept loc
00377 itsSCeye->setPercept(win, q);
00378
00379 // the head follows the eye:
00380 win.p = itsSCeye->getPreviousDecision(0).p;
00381 itsSChead->setPercept(win, q);
00382 }
00383 }
00384 else
00385 {
00386 // we have no recommendation from itsTSC
00387
00388 // set friction targets: the eye follows the moving average if
00389 // the recent percepts are nicely clustered, otherwise the eyes
00390 // just don't move:
00391 bool areclose; Point2D<int> avgp;
00392 WTAwinner win(fix);
00393 itsTSC->checkPercepts(areclose, avgp);
00394 if (areclose)
00395 {
00396 LINFO("=== Smooth pursuit to moving avg (%d, %d) at %.2fms ===",
00397 avgp.i, avgp.j, win.t.msecs());
00398 win.p = avgp; // use target location from itsTSC as percept loc
00399 itsSCeye->setPercept(win, q);
00400 }
00401
00402 // the head follows the eye:
00403 win.p = itsSCeye->getPreviousDecision(0).p;
00404 itsSChead->setPercept(win, q);
00405 }
00406 */
00407 }
00408
00409
00410
00411
00412
00413
00414
00415
00416
00417
00418
00419
00420
00421
00422
00423
00424
00425
00426
00427
00428
00429
00430
00431
00432
00433
00434
00435
00436
00437
00438
00439
00440
00441
00442
00443
00444 double MonkeyEyeHeadController::headAmplitude(const double curreye,
00445 const double currhead,
00446 const double target)
00447 {
00448
00449
00450
00451
00452
00453 double iep = (curreye - currhead) * signOf(target - curreye);
00454
00455
00456 double hgazedd = target - curreye;
00457
00458
00459
00460
00461 double hhdz = 0.56 * (20.0 - 0.5 * iep); if (hhdz < 0.0) hhdz = 0.0;
00462
00463
00464
00465
00466
00467 double hheaddd = 0.0, hslh = 0.0;
00468
00469 if (fabs(hgazedd) >= hhdz)
00470 {
00471
00472
00473
00474
00475
00476 hslh = 0.65 * (fabs(iep) / 35.0 + 1.1); if (hslh > 1.0) hslh = 1.0;
00477 hheaddd = hgazedd * hslh + hhdz * (1.0 - hslh) * signOf(hgazedd);
00478 }
00479
00480 LINFO("curreye=%f currhead=%f target=%f iep=%f hgazedd=%f hhdz=%f "
00481 "hslh=%f hheaddd=%f", curreye, currhead, target, iep, hgazedd, hhdz,
00482 hslh, hheaddd);
00483 return hheaddd;
00484 }
00485
00486
00487
00488
00489
00490
00491
00492
00493