00001 /*!@file Robots/Beobot2/Navigation/FOE_Navigation/test-FOE.C 00002 find the focus of expansion */ 00003 // //////////////////////////////////////////////////////////////////// // 00004 // The iLab Neuromorphic Vision C++ Toolkit - Copyright (C) 2001 by the // 00005 // 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: Christian Siagian <siagian@usc.edu> 00034 // $HeadURL: $ 00035 // $Id: $ 00036 // 00037 00038 #include "Component/ModelManager.H" 00039 #include "Component/OptionManager.H" 00040 #include "Devices/FrameGrabberConfigurator.H" 00041 00042 #include "Media/FrameSeries.H" 00043 00044 #include "Image/ColorOps.H" 00045 #include "Image/ShapeOps.H" 00046 #include "Image/Image.H" 00047 #include "Image/Layout.H" 00048 #include "Raster/Raster.H" 00049 00050 #include "SIFT/Keypoint.H" 00051 #include "SIFT/VisualObject.H" 00052 #include "SIFT/VisualObjectMatch.H" 00053 00054 #include "Transport/FrameInfo.H" 00055 #include "Util/Pause.H" 00056 #include "Util/Timer.H" 00057 #include "Util/Types.H" 00058 #include "Util/csignals.H" 00059 #include "Util/log.H" 00060 #include <math.h> 00061 #include <cstdio> 00062 #include "Robots/Beobot2/Navigation/FOE_Navigation/FoeDetector.H" 00063 00064 00065 #define DOT_NUM 200 // 200 1000 00066 00067 #define WIDTH 320 // 320 640 00068 #define HEIGHT 240 // 240 480 00069 #define FOE_X 2*WIDTH/4 // W/4 00070 #define FOE_Y 2*HEIGHT/4 // 2H/4 +4 00071 00072 #define DOT_VEL 2.0/80.0 // Shuo: .035 00073 00074 00075 // Shuo: in PsychToolbox: size refers to diameter, in our code radius 00076 // Shuo: Abs max: 21, ave DS is 9.767 across all dots in each frames 00077 #define DOT_ORG_DSIZE .02 // .04 00078 #define DOT_DSIZE .07 // .035 00079 #define MIN_DOT_SIZE 1 00080 #define MAX_DOT_SIZE 5 00081 #define ABS_MAX_DSIZE 50 00082 #define NFRAME 60 00083 00084 #define HAVE_MOTION 1 00085 #define HAVE_TEMP_SGRAD 1 00086 #define HAVE_SPAT_SGRAD 1 00087 00088 #define NUM_PYR_LEVEL 2 // 3 for 640x480 00089 #define NUM_DIRS 8 00090 #define NUM_SPEEDS 3 00091 00092 // get the ground truth from a text file 00093 std::vector<Point2D<int> > getGT(std::string gtFilename); 00094 00095 Image<byte> calculateShift 00096 (Image<byte> lum, Image<byte> prevLum, nub::ref<OutputFrameSeries> ofs); 00097 00098 // get the image for stimuli 00099 Image<byte> getFoeDots 00100 (uint step, bool haveMotion, bool haveTempSGrad, bool haveSpatSGrad, 00101 float dx, float dy, float dotOrgDSize); 00102 00103 Image<byte> getPlanarMotionStimuli 00104 (Image<byte> temp, uint step, float dx, float dy); 00105 00106 Image<byte> getBarStimuli(uint step); 00107 Image<byte> getShapeStimuli(uint step); 00108 Image<byte> getApertureProblemStimuli(uint step); 00109 00110 // this is stimuli like in Fukuchi,Tsuchiya,Koch VSS/JOV 2009/2010 00111 // FOE is FOE_X,FOE_Y 00112 Image<byte> getCleanFOE 00113 (uint step, uint totalStep, uint mag, float dx, float dy, Image<byte> image); 00114 00115 Image<byte> getPlanarMotionImage 00116 (uint step, uint totalStep, float dx, float dy, Image<byte> image); 00117 00118 Image<byte> shiftImage(SIFTaffine aff, Image<byte> ref, Image<byte> tst); 00119 00120 Image<byte> getImage 00121 (std::string stimuli, std::vector<std::string> args, 00122 nub::ref<FoeDetector> fd, uint step); 00123 00124 // ###################################################################### 00125 std::vector<Point2D<float> > dots; 00126 std::vector<float> dotSizes; 00127 00128 std::vector<Point2D<float> > pdots; 00129 std::vector<float> pdotSizes; 00130 00131 // ###################################################################### 00132 int main(const int argc, const char **argv) 00133 { 00134 MYLOGVERB = LOG_INFO; // suppress debug messages 00135 00136 volatile int signum = 0; 00137 catchsignals(&signum); 00138 00139 ModelManager manager("Test Motion Energy"); 00140 00141 nub::ref<InputFrameSeries> ifs(new InputFrameSeries(manager)); 00142 manager.addSubComponent(ifs); 00143 00144 nub::ref<OutputFrameSeries> ofs(new OutputFrameSeries(manager)); 00145 manager.addSubComponent(ofs); 00146 00147 nub::ref<FoeDetector> fd(new FoeDetector(manager)); 00148 manager.addSubComponent(fd); 00149 00150 if (manager.parseCommandLine((const int)argc, (const char**)argv, 00151 "<stimuli> <options>", 0, 9) == false) 00152 return(1); 00153 00154 fd->reset(NUM_PYR_LEVEL, NUM_DIRS, NUM_SPEEDS); 00155 00156 std::string stimuli("Image"); 00157 if(manager.numExtraArgs() > 0) 00158 stimuli = manager.getExtraArgAs<std::string>(0); 00159 LINFO("Stimuli: %s", stimuli.c_str()); 00160 00161 manager.start(); 00162 00163 Timer timer(1000000); 00164 timer.reset(); // reset the timer 00165 int frame = 0; 00166 00167 PauseWaiter p; 00168 00169 uint step; step = 0; 00170 00171 // to get to the good part 00172 //for(uint i = 0; i < 50; i++) //was 25 00173 // ifs->updateNext(); 00174 00175 // get ground truth file 00176 std::string gtFilename 00177 ("/lab/tmpib/u/siagian/neuroscience/Data/FOE/driving_nat_Browning.txt"); 00178 std::vector<Point2D<int> > gt = getGT(gtFilename); 00179 int ldpos = gtFilename.find_last_of('.'); 00180 std::string prefix = gtFilename.substr(0, ldpos); 00181 00182 // for finding ground truth 00183 rutz::shared_ptr<XWinManaged> win; 00184 00185 float totalErr = 0.0; 00186 00187 std::vector<std::string> args; 00188 for(uint i = 0; i < manager.numExtraArgs(); i++) 00189 args.push_back(manager.getExtraArgAs<std::string>(i)); 00190 00191 Image<byte> prevLum; 00192 Image<PixRGB<byte> > prevImage; 00193 Image<PixRGB<byte> > prevImage2; 00194 while (1) 00195 { 00196 if (signum != 0) 00197 { 00198 LINFO("quitting because %s was caught", signame(signum)); 00199 break; 00200 } 00201 00202 if (ofs->becameVoid()) 00203 { 00204 LINFO("quitting because output stream was closed or became void"); 00205 break; 00206 } 00207 00208 if (p.checkPause()) 00209 continue; 00210 00211 const FrameState is = ifs->updateNext(); 00212 if (is == FRAME_COMPLETE) break; // done receiving frames 00213 00214 Image< PixRGB<byte> > input = ifs->readRGB(); 00215 if(frame == 0) 00216 { 00217 uint width = input.getWidth(); 00218 uint height = input.getHeight(); 00219 win.reset(new XWinManaged(Dims(width, height), 0, 0, "GT")); 00220 } 00221 00222 // empty image signifies end-of-stream 00223 if (!input.initialized()) break; 00224 Image<byte> lum = luminance(input); 00225 Point2D<float> pshift(0.0,0.0); 00226 if(step != 0) 00227 { 00228 // calculate planar shift using SIFT 00229 lum = calculateShift(lum,prevLum, ofs); 00230 } 00231 if( manager.numExtraArgs() > 0) 00232 lum = getImage(stimuli, args, fd, step); 00233 00234 // for saving videos 00235 prevImage2 = prevImage; 00236 prevImage = input; 00237 00238 if (!lum.initialized()) break; step++; 00239 00240 // compute the focus of expansion (FOE) 00241 Point2D<int> foe = fd->getFoe(lum, FOE_METHOD_TEMPLATE, false); 00242 //Point2D<int> foe = fd->getFoe(lum, FOE_METHOD_AVERAGE); 00243 LINFO("[%d]Foe: %d %d", frame, foe.i, foe.j); 00244 00245 // illustration of the size of the receptive field 00246 if(!stimuli.compare("ShowRF")) 00247 { 00248 uint rfI = 44; 00249 uint rfJ = 152; 00250 lum.setVal(rfI, rfJ, 300.0F); 00251 drawRect(lum, Rectangle::tlbrI(144,36,159,51), byte(255)); 00252 drawRect(lum, Rectangle::tlbrI(148,40,155,47), byte(255)); 00253 00254 drawRect(lum, Rectangle::tlbrI(rfJ-8, rfI-8, rfJ+8, rfI+8), byte(255)); 00255 drawRect(lum, Rectangle::tlbrI(rfJ-16,rfI-16,rfJ+16,rfI+16), byte(255)); 00256 } 00257 00258 ofs->writeGrayLayout(fd->getMTfeaturesDisplay(lum), "MT Features", 00259 FrameInfo("motion energy output images", SRC_POS)); 00260 00261 // write the file 00262 if(frame >= 4) 00263 { 00264 float err = foe.distance(gt[frame-2]); 00265 totalErr += err; 00266 LINFO("Foe: %d %d: GT: %d %d --> %f --> avg: %f", 00267 foe.i, foe.j, gt[frame-2].i, gt[frame-2].j, 00268 err, totalErr/(frame-3)); 00269 00270 Image<PixRGB<byte> > simg = prevImage2; 00271 drawCross(simg, foe , PixRGB<byte>(0,255,0), 10, 2); 00272 drawCross(simg, gt[frame-2], PixRGB<byte>(255,0,0), 10, 2); 00273 win->drawImage(simg,0,0); 00274 //Raster::WriteRGB(simg, sformat("%s_STnPS_%06d.ppm", prefix.c_str(), frame-2)); 00275 } 00276 00277 //ofs->writeGrayLayout 00278 // (lum, "test-FOE Main", FrameInfo("foe output", SRC_POS)); 00279 const FrameState os = ofs->updateNext(); 00280 //LINFO("frame[%d]: %8.3f %8.3f", frame, pshift.i, pshift.j); 00281 Raster::waitForKey(); 00282 00283 if (os == FRAME_FINAL) 00284 break; 00285 00286 prevLum = lum; 00287 frame++; 00288 } 00289 00290 LINFO("%d frames in %gs (%.2ffps)\n", 00291 frame, timer.getSecs(), frame / timer.getSecs()); 00292 00293 // stop all our ModelComponents 00294 manager.stop(); 00295 00296 // all done! 00297 return 0; 00298 00299 } 00300 00301 // ###################################################################### 00302 std::vector<Point2D<int> > getGT(std::string gtFilename) 00303 { 00304 std::vector<Point2D<int> > gt; 00305 00306 FILE *fp; char inLine[200]; //char comment[200]; 00307 00308 LINFO("ground truth file: %s",gtFilename.c_str()); 00309 if((fp = fopen(gtFilename.c_str(),"rb")) == NULL) 00310 { LINFO("not found"); return gt; } 00311 00312 // populate the trial information 00313 uint nFrames = 0; 00314 while(fgets(inLine, 200, fp) != NULL) 00315 { 00316 int x, y; 00317 sscanf(inLine, "%d %d", &x, &y); 00318 LDEBUG("[%3d] x: %d y: %d", nFrames, x, y); 00319 gt.push_back(Point2D<int>(x,y)); 00320 00321 nFrames++; 00322 } 00323 return gt; 00324 } 00325 00326 // ###################################################################### 00327 Image<byte> getImage 00328 (std::string stimuli, std::vector<std::string> args, 00329 nub::ref<FoeDetector> fd, uint step) 00330 { 00331 Image<byte> lum; 00332 00333 // Different stimuli: 00334 if(!stimuli.compare("Image")) 00335 { 00336 float di = 0.0; float dj = 0.0; 00337 //float di = -3.6; float dj = -.6; // first frame ACB 00338 if(args.size() > 2) 00339 { 00340 di = atof(args[1].c_str()); 00341 dj = atof(args[2].c_str()); 00342 //fd->setObserverRotation(di, dj); 00343 } 00344 LDEBUG("di: %f, dj: %f", di, dj); 00345 } 00346 else if(!stimuli.compare("BarStimuli")) 00347 lum = getBarStimuli(step); 00348 else if(!stimuli.compare("ApertureProblem")) 00349 lum = getApertureProblemStimuli(step); 00350 else if(!stimuli.compare("CleanFoe")) 00351 { 00352 uint total = 30; // 15 for the Fukuchi, et. al. paper 00353 if(args.size() > 1) 00354 total = uint(atoi(args[1].c_str())); 00355 float dx = 0.0; 00356 float dy = 0.0; 00357 if(args.size() > 3) 00358 { 00359 dx = atof(args[2].c_str()); 00360 dy = atof(args[3].c_str()); 00361 } 00362 lum = getCleanFOE(step, total, 2.0, dx, dy, lum); 00363 00364 // float di = 0.0; float dj = 0.0; 00365 // if(manager.numExtraArgs() > 5) 00366 // { 00367 // di = manager.getExtraArgAs<float>(4); 00368 // dj = manager.getExtraArgAs<float>(5); 00369 // fd->setObserverRotation(di, dj); 00370 // } 00371 // planar (Yaw-Pitch) motion correction 00372 uint dir = 0.0; float speed = 0.0; 00373 if(args.size() > 5) 00374 { 00375 dir = uint(atoi(args[4].c_str())); 00376 speed = atof(args[5].c_str()); 00377 fd->setObserverRotation(dir, speed); 00378 } 00379 } 00380 else if(!stimuli.compare("FoeDots")) 00381 { 00382 bool haveMotion = HAVE_MOTION; 00383 bool haveTempSGrad = HAVE_TEMP_SGRAD; 00384 bool haveSpatSGrad = HAVE_SPAT_SGRAD; 00385 if(args.size() > 5) 00386 { 00387 haveMotion = bool(atoi(args[1].c_str())); 00388 haveTempSGrad = bool(atoi(args[2].c_str())); 00389 haveSpatSGrad = bool(atoi(args[3].c_str())); 00390 } 00391 00392 float dx = 0.0; float dy = 0.0; 00393 //float dx = pshift.i; float dy = pshift.j; 00394 if(args.size() > 5) 00395 { 00396 dx = atof(args[4].c_str()); 00397 dy = atof(args[5].c_str()); 00398 } 00399 00400 float dotOrgDSize = DOT_ORG_DSIZE; 00401 if(args.size() > 6) 00402 { 00403 dotOrgDSize = atof(args[6].c_str()); 00404 } 00405 00406 // planar (Yaw-Pitch) motion correction 00407 uint dir = 0.0; float speed = 0.0; 00408 if(args.size() > 8) 00409 { 00410 dir = uint(atoi(args[7].c_str())); 00411 speed = atof(args[8].c_str()); 00412 fd->setObserverRotation(dir, speed); 00413 } 00414 00415 LINFO("[%3d] haveMotion: %d haveTempSGrad: %d haveSpatSGrad: %d " 00416 "dx dy: %7.3f %7.3f dotOrgDSize: %f comp: %d %f", 00417 step, haveMotion, haveTempSGrad, haveSpatSGrad, 00418 dx, dy, dotOrgDSize, dir, speed); 00419 lum = getFoeDots(step, haveMotion, haveTempSGrad, haveSpatSGrad, 00420 dx, dy, dotOrgDSize); 00421 00422 //float px = 2.0; float py = 0.0; 00423 // if(manager.numExtraArgs() > 2) 00424 // { 00425 // dx = manager.getExtraArgAs<float>(1); 00426 // dy = manager.getExtraArgAs<float>(2); 00427 // } 00428 //lum = getPlanarMotionStimuli(lum, step, px, py); 00429 } 00430 else if(!stimuli.compare("PlanarDots")) 00431 { 00432 Image<byte> temp(WIDTH, HEIGHT, ZEROS); 00433 00434 float dx = 0.0; float dy = 0.0; 00435 if(args.size() > 2) 00436 { 00437 dx = atof(args[1].c_str()); 00438 dy = atof(args[2].c_str()); 00439 } 00440 lum = getPlanarMotionStimuli(temp, step, dx, dy); 00441 } 00442 else if(!stimuli.compare("PlanarImage")) 00443 { 00444 uint total = 30; // 15 for the Fukuchi, et. al. paper 00445 float dx = 0.0; float dy = 0.0; 00446 if(args.size() > 2) 00447 { 00448 dx = atof(args[1].c_str()); 00449 dy = atof(args[2].c_str()); 00450 } 00451 lum = getPlanarMotionImage(step, total, dx, dy, lum); 00452 } 00453 else LFATAL("Wrong option"); 00454 00455 return lum; 00456 } 00457 00458 // ###################################################################### 00459 Image<byte> calculateShift(Image<byte> lum, Image<byte> prevLum, 00460 nub::ref<OutputFrameSeries> ofs) 00461 { 00462 VisualObjectMatchAlgo voma(VOMA_SIMPLE); 00463 // if (strcmp(argv[1], "KDTree") == 0) voma = VOMA_KDTREE; 00464 // else if (strcmp(argv[1], "KDBBF") == 0) voma = VOMA_KDTREEBBF; 00465 // else if (strcmp(argv[1], "Simple") != 0) 00466 // LFATAL("Unknown matching method %s", argv[0]); 00467 00468 // create visual objects: 00469 rutz::shared_ptr<VisualObject> vo1(new VisualObject("lum", "", lum)); 00470 rutz::shared_ptr<VisualObject> vo2(new VisualObject("plum", "", prevLum)); 00471 00472 // compute the matching keypoints: 00473 VisualObjectMatch match(vo1, vo2, voma); 00474 LDEBUG("Found %u matches", match.size()); 00475 00476 // let's prune the matches: 00477 uint np = match.prune(); 00478 LDEBUG("Pruned %u outlier matches.", np); 00479 00480 // show our final affine transform: 00481 SIFTaffine s = match.getSIFTaffine(); 00482 LDEBUG("[tstX] [ %- .3f %- .3f ] [refX] [%- .3f]", s.m1, s.m2, s.tx); 00483 LDEBUG("[tstY] = [ %- .3f %- .3f ] [refY] + [%- .3f]", s.m3, s.m4, s.ty); 00484 00485 00486 LDEBUG("getKeypointAvgDist = %f", match.getKeypointAvgDist()); 00487 LDEBUG("getAffineAvgDist = %f", match.getAffineAvgDist()); 00488 LDEBUG("getScore = %f", match.getScore()); 00489 00490 if (match.checkSIFTaffine() == false) 00491 LINFO("### Affine is too weird -- BOGUS MATCH"); 00492 00493 // get an image showing the matches: 00494 Image< PixRGB<byte> > mimg = match.getMatchImage(1.0F); 00495 Image< PixRGB<byte> > fimg = match.getFusedImage(0.25F); 00496 00497 // LINFO("lum"); 00498 // ofs->writeRGB 00499 // (toRGB(lum), "test-FOE Main", FrameInfo("foe output", SRC_POS)); 00500 // Raster::waitForKey(); 00501 00502 // LINFO("prevLum"); 00503 // ofs->writeRGB 00504 // (toRGB(prevLum), "test-FOE Main", FrameInfo("foe output", SRC_POS)); 00505 // Raster::waitForKey(); 00506 00507 00508 LINFO("Shift: %f %f", s.tx, s.ty); 00509 // ofs->writeRGB 00510 // (fimg, "test-FOE Main", FrameInfo("foe output", SRC_POS)); 00511 // Raster::waitForKey(); 00512 00513 Image<byte> res = shiftImage(s, lum, prevLum); 00514 00515 // LINFO("shifted result"); 00516 // ofs->writeRGB 00517 // (toRGB(res), "test-FOE Main", FrameInfo("foe output", SRC_POS)); 00518 // Raster::waitForKey(); 00519 00520 //Point2D<float> shift(s.tx,s.ty); 00521 return res; 00522 } 00523 00524 // ###################################################################### 00525 Image<byte> shiftImage(SIFTaffine aff, Image<byte> refi, Image<byte> tsti) 00526 { 00527 // we loop over all pixel locations in the ref image, transform the 00528 // coordinates using the forward affine transform, get the pixel 00529 // value in the test image, and mix: 00530 00531 uint w = refi.getWidth(), h = refi.getHeight(); 00532 Image<byte> result(w, h, ZEROS); 00533 Image<byte>::const_iterator rptr = refi.begin(); 00534 Image<byte>::iterator dptr = result.beginw(); 00535 00536 for (uint j = 0; j < h; j ++) 00537 for (uint i = 0; i < w; i ++) 00538 { 00539 float u, v; 00540 aff.transform(float(i), float(j), u, v); 00541 byte rval = *rptr++; 00542 00543 if (tsti.coordsOk(u, v)) 00544 { 00545 byte tval = tsti.getValInterp(u, v); 00546 //PixRGB<byte> mval = PixRGB<byte>(rval * mix + tval * (1.0F - mix)); 00547 *dptr++ = tval; 00548 } 00549 else 00550 //*dptr++ = PixRGB<byte>(rval * mix); 00551 *dptr++ = byte(rval); 00552 } 00553 00554 return result; 00555 } 00556 00557 00558 // ###################################################################### 00559 Image<byte> getApertureProblemStimuli(uint step) 00560 { 00561 Image<byte> temp(WIDTH, HEIGHT, ZEROS); 00562 00563 // create dot initially 00564 if(step == 0) 00565 { 00566 dots.resize(1); 00567 // top left 00568 dots[0] = Point2D<float> (WIDTH/2, HEIGHT/4); 00569 } 00570 00571 uint len = 15; 00572 // APERTURE PROBLEM 00573 //=============================================== 00574 dots[0] = Point2D<float>(dots[0].i + 2.0, dots[0].j); 00575 drawLine 00576 (temp, 00577 Point2D<int>(dots[0].i-len, dots[0].j-len), 00578 Point2D<int>(dots[0].i+len, dots[0].j+len), byte(255), 1); 00579 00580 return temp; 00581 } 00582 00583 // ###################################################################### 00584 Image<byte> getShapeStimuli(uint step) 00585 { 00586 Image<byte> temp(WIDTH, HEIGHT, ZEROS); 00587 // moving left 00588 // Rectangle r(Point2D<int>(100,100), Dims(20,20)); 00589 // //Rectangle r(Point2D<int>(0+step*10,0+step*10), Dims(20,20)); 00590 // if(step%2 ==0) 00591 // drawFilledRect(temp,r,byte(255)); 00592 // //if(step%2 ==0) 00593 // //drawDisk(temp,Point2D<int>(100+step*1,100+step*1),5,byte(255)); 00594 return temp; 00595 } 00596 00597 // ###################################################################### 00598 Image<byte> getBarStimuli(uint step) 00599 { 00600 Image<byte> temp(WIDTH, HEIGHT, ZEROS); 00601 00602 // initially create dots 00603 if(step == 0) 00604 { 00605 dots.resize(4); dotSizes.resize(4); 00606 00607 // top left 00608 dots[0] = Point2D<float> (WIDTH/2, HEIGHT/4); 00609 dotSizes[0] = 1.0; 00610 dots[1] = Point2D<float> (WIDTH/4, HEIGHT/2); 00611 dotSizes[1] = 1.0; 00612 dots[2] = Point2D<float> (WIDTH/8, HEIGHT/4); 00613 dotSizes[2] = 1.0; 00614 dots[3] = Point2D<float> (WIDTH/4, HEIGHT/8); 00615 dotSizes[3] = 1.0; 00616 00617 // bottom right 00618 // dots[0] = Point2D<float> (7*WIDTH/8, 3*HEIGHT/4); 00619 // dotSizes[0] = 1.0; 00620 // dots[1] = Point2D<float> (3*WIDTH/4, 7*HEIGHT/8); 00621 // dotSizes[1] = 1.0; 00622 // dots[2] = Point2D<float> (WIDTH/2, 3*HEIGHT/4); 00623 // dotSizes[2] = 1.0; 00624 // dots[3] = Point2D<float> (3*WIDTH/4, HEIGHT/2); 00625 // dotSizes[3] = 1.0; 00626 00627 } 00628 00629 // move the dots if needed 00630 if(HAVE_MOTION) 00631 { 00632 //for(uint i = 0; i < dotNum; i++) 00633 dots[0] = Point2D<float>(dots[0].i + 2.0, dots[0].j); 00634 dots[1] = Point2D<float>(dots[1].i, dots[1].j + 1.0); 00635 dots[2] = Point2D<float>(dots[2].i - 1.0, dots[2].j); 00636 dots[3] = Point2D<float>(dots[3].i, dots[3].j - 1.0); 00637 00638 //LINFO("loc: %f %f: size: %f", dots[i].i, dots[i].j, dotSizes[i]); 00639 } 00640 // finally draw the dots 00641 //for(uint i = 0; i < dotNum; i++) 00642 //drawDisk(temp,Point2D<int>(dots[i].i, dots[i].j),dotSizes[i],byte(255)); 00643 00644 //drawDisk(temp,Point2D<int>(dots[0].i, dots[0].j), 1.0,byte(255)); 00645 //drawDisk(temp,Point2D<int>(dots[1].i, dots[1].j), 1.0,byte(255)); 00646 //drawDisk(temp,Point2D<int>(dots[2].i, dots[2].j), 1.0,byte(255)); 00647 //drawDisk(temp,Point2D<int>(dots[3].i, dots[3].j), 1.0,byte(255)); 00648 00649 drawLine 00650 (temp, 00651 Point2D<int>(dots[0].i, dots[0].j-15), 00652 Point2D<int>(dots[0].i, dots[0].j+15), byte(255), 1); 00653 00654 // control the thickness of the line 00655 // drawLine 00656 // (temp, 00657 // Point2D<int>(dots[0].i, dots[0].j-15), 00658 // Point2D<int>(dots[0].i, dots[0].j+15), byte(255), 1); 00659 // drawLine 00660 // (temp, 00661 // Point2D<int>(dots[0].i+1, dots[0].j-15), 00662 // Point2D<int>(dots[0].i+1, dots[0].j+15), byte(255), 1); 00663 // drawLine 00664 // (temp, 00665 // Point2D<int>(dots[0].i-1, dots[0].j-15), 00666 // Point2D<int>(dots[0].i-1, dots[0].j+15), byte(255), 1); 00667 // drawLine 00668 // (temp, 00669 // Point2D<int>(dots[0].i-2, dots[0].j-15), 00670 // Point2D<int>(dots[0].i-2, dots[0].j+15), byte(255), 1); 00671 // drawLine 00672 // (temp, 00673 // Point2D<int>(dots[0].i-3, dots[0].j-15), 00674 // Point2D<int>(dots[0].i-3, dots[0].j+15), byte(255), 1); 00675 // drawLine 00676 // (temp, 00677 // Point2D<int>(dots[0].i+2, dots[0].j-15), 00678 // Point2D<int>(dots[0].i+2, dots[0].j+15), byte(255), 1); 00679 // drawLine 00680 // (temp, 00681 // Point2D<int>(dots[0].i-4, dots[0].j-15), 00682 // Point2D<int>(dots[0].i-4, dots[0].j+15), byte(255), 1); 00683 // drawLine 00684 // (temp, 00685 // Point2D<int>(dots[0].i+3, dots[0].j-15), 00686 // Point2D<int>(dots[0].i+3, dots[0].j+15), byte(255), 1); 00687 00688 //=============================================== 00689 // drawLine 00690 // (temp, 00691 // Point2D<int>(dots[1].i-15, dots[1].j), 00692 // Point2D<int>(dots[1].i+15, dots[1].j), byte(255), 1); 00693 00694 // drawLine 00695 // (temp, 00696 // Point2D<int>(dots[2].i, dots[2].j-15), 00697 // Point2D<int>(dots[2].i, dots[2].j+15), byte(255), 1); 00698 00699 // drawLine 00700 // (temp, 00701 // Point2D<int>(dots[3].i-15, dots[3].j), 00702 // Point2D<int>(dots[3].i+15, dots[3].j), byte(255), 1); 00703 00704 00705 //drawDisk(temp,Point2D<int>(dots[i].i, dots[i].j),dotSizes[i],byte(255)); 00706 //temp.setVal(dots[i].i, dots[i].j, byte(128)); 00707 00708 //drawDisk(temp,Point2D<int>(FOE_X,FOE_Y),5,byte(128)); 00709 00710 return temp; 00711 } 00712 00713 // ###################################################################### 00714 Image<byte> getPlanarMotionStimuli 00715 (Image<byte> temp, uint step, float dx, float dy) 00716 { 00717 //if(step > NFRAME) return Image<byte>(); 00718 00719 uint dotNum = 200;//200;//DOT_NUM; 00720 00721 float orgDotSize = 1.0; 00722 00723 // create random dot initially 00724 if(step == 0) 00725 { 00726 pdots.resize(dotNum); pdotSizes.resize(dotNum); 00727 for(uint i = 0; i < dotNum; i++) 00728 { 00729 pdots[i] = Point2D<float> 00730 (WIDTH * double(rand())/(RAND_MAX + 1.0), 00731 HEIGHT * double(rand())/(RAND_MAX + 1.0)); 00732 pdotSizes[i] = orgDotSize; 00733 } 00734 } 00735 00736 // check for out-of-bounds dot needed to be shown 00737 for(uint i = 0; i < dotNum; i++) 00738 { 00739 // NOTE: can also kill dots randomly before going out of the image 00740 if(!temp.getBounds().contains(Point2D<int>(pdots[i].i, pdots[i].j))) 00741 { 00742 float srx = 0.0; float sry = 0.0; 00743 float rx = WIDTH; float ry = HEIGHT; 00744 if(dx < 0.0) { srx = 7.0*WIDTH/8.0; rx = WIDTH/8; } 00745 else if(dx > 0.0){ srx = 0.0; rx = WIDTH/8; } 00746 00747 if(dy < 0.0) { sry = 7.0*HEIGHT/8.0; ry = HEIGHT/8; } 00748 else if(dy > 0.0){ sry = 0.0; ry = HEIGHT/8; } 00749 00750 float sx = rx * double(rand())/(RAND_MAX + 1.0) + srx; 00751 float sy = ry * double(rand())/(RAND_MAX + 1.0) + sry; 00752 00753 pdots[i] = Point2D<float>(sx,sy); 00754 pdotSizes[i] = orgDotSize; 00755 LDEBUG("new dots[%3d]: %7.3f %7.3f", i, sx, sy); 00756 } 00757 else 00758 { 00759 Point2D<int> pt(pdots[i].i, pdots[i].j); 00760 LDEBUG("[%3d] it's ok: (%7.3f %7.3f) -> %3d %3d", 00761 i, pdots[i].i, pdots[i].j, pt.i, pt.j); 00762 } 00763 } 00764 00765 // planar motion 00766 if(dx != 0.0 || dy != 0.0) 00767 for(uint i = 0; i < dotNum; i++) 00768 { 00769 pdots[i] = Point2D<float>(pdots[i].i + dx, 00770 pdots[i].j + dy); 00771 } 00772 00773 // finally draw the dots 00774 for(uint i = 0; i < dotNum; i++) 00775 { 00776 LDEBUG("[%d] loc: %10.3f %10.3f: size: %7.3f", 00777 i, pdots[i].i, pdots[i].j, pdotSizes[i]); 00778 drawDisk(temp,Point2D<int>(pdots[i].i, pdots[i].j), 00779 pdotSizes[i],byte(255)); 00780 //temp.setVal(pdots[i].i, pdots[i].j, byte(128)); 00781 } 00782 return temp; 00783 } 00784 00785 // ###################################################################### 00786 Image<byte> orgPlanarImage; 00787 Image<byte> getPlanarMotionImage 00788 (uint step, uint totalStep, float dx, float dy, Image<byte> image) 00789 { 00790 // just loop it 00791 step = step % totalStep; 00792 00793 // set original image on first step 00794 if(step == 0) 00795 { 00796 orgPlanarImage = image; 00797 } 00798 image = orgPlanarImage; 00799 00800 uint width = image.getWidth(); 00801 uint height = image.getHeight(); 00802 float scale = 1.25; 00803 Image<byte> temp = rescale(image, scale*width, scale*height); 00804 float nwidth = temp.getWidth(); 00805 float nheight = temp.getHeight(); 00806 00807 float sleft = 0.0; if(dx < 0.0) sleft = nwidth - 1 - width; 00808 float stop = 0.0; if(dy < 0.0) stop = nheight - 1 - height; 00809 00810 float left = sleft + dx*step; 00811 float top = stop + dy*step; 00812 00813 //if(top < 0.0) top = 0.0; 00814 //if(left < 0.0) left = 0.0; 00815 00816 Rectangle r = 00817 Rectangle::tlbrI(top, left, top+height-1, left+width-1); 00818 00819 // LINFO("[%3d/%3d] FOE(%7.3f %7.3f) %f p(%7.3f %7.3f) [[%7.3f %7.3f]] " 00820 // "[%3d %3d %3d %3d] temp(%3d %3d) ((%3d %3d))", 00821 // step, totalStep, 00822 // foeX,foeY, scale, px, py, top, left, 00823 // r.top(), r.left(), r.bottomI(), r.rightI(), 00824 // temp.getWidth(), temp.getHeight(), 00825 // r.width(),r.height()); 00826 Image<byte> result = crop(temp, r); 00827 00828 return result; 00829 } 00830 00831 // ###################################################################### 00832 Image<byte> getFoeDots 00833 (uint step, bool haveMotion, bool haveTempSGrad, bool haveSpatSGrad, 00834 float dx, float dy, float dotOrgDSize) 00835 { 00836 //if(step > NFRAME) return Image<byte>(); 00837 00838 Image<byte> temp(WIDTH, HEIGHT, ZEROS); 00839 00840 float orgDotSize = 1.0; 00841 00842 // create random dot initially 00843 if(step == 0) 00844 { 00845 dots.resize(DOT_NUM); dotSizes.resize(DOT_NUM); 00846 for(uint i = 0; i < DOT_NUM; i++) 00847 { 00848 dots[i] = Point2D<float> 00849 (WIDTH * double(rand())/(RAND_MAX + 1.0), 00850 HEIGHT * double(rand())/(RAND_MAX + 1.0)); 00851 00852 // just do it in order to get identical average size 00853 //float range = MAX_DOT_SIZE - MIN_DOT_SIZE; 00854 //dotSizes[i] = i*range/(DOT_NUM-1.0)+ double(MIN_DOT_SIZE); 00855 dotSizes[i] = orgDotSize; 00856 } 00857 } 00858 00859 // check for out-of-bounds dot needed to be shown 00860 for(uint i = 0; i < DOT_NUM; i++) 00861 { 00862 // NOTE: can also kill dots randomly before going out of the image 00863 // NOTE: how about adding new dots in the FOE quadrants 00864 if(!temp.getBounds().contains(Point2D<int>(dots[i].i, dots[i].j))) 00865 { 00866 dots[i] = Point2D<float> 00867 (WIDTH * double(rand())/(RAND_MAX + 1.0), 00868 HEIGHT * double(rand())/(RAND_MAX + 1.0) ); 00869 00870 // keep the sizes or 1.0? 00871 dotSizes[i] = orgDotSize; 00872 } 00873 } 00874 00875 // modify sizes according to rules 00876 for(uint i = 0; i < DOT_NUM; i++) 00877 { 00878 if(haveTempSGrad && haveSpatSGrad) 00879 { 00880 float dist = sqrt(pow((dots[i].i - FOE_X), 2.0) + 00881 pow((dots[i].j - FOE_Y), 2.0) ); 00882 if(haveMotion) 00883 { 00884 dotSizes[i] = dotOrgDSize * dist; 00885 } 00886 // growing, FOE coherent, but not moving 00887 else 00888 { 00889 if(step == 0) 00890 { 00891 dotSizes[i] = dotOrgDSize * dist; 00892 } 00893 else 00894 { 00895 // increase until ABS_MAX_DSIZE, then stop 00896 if(dotSizes[i] < ABS_MAX_DSIZE) 00897 { 00898 dotSizes[i] = (1.0 + DOT_DSIZE) * 00899 dotOrgDSize * dist; 00900 } 00901 // else dot size stays the same 00902 } 00903 } 00904 } 00905 else if(haveTempSGrad && !haveSpatSGrad) 00906 { 00907 // all dot have same size just increase 00908 float ds = MAX_DOT_SIZE - MIN_DOT_SIZE; 00909 dotSizes[i] = step*ds/(NFRAME - 1.0)+ double(MIN_DOT_SIZE); 00910 } 00911 else if(!haveTempSGrad && haveSpatSGrad) 00912 { 00913 float dist = sqrt(pow((dots[i].i - FOE_X), 2.0) + 00914 pow((dots[i].j - FOE_Y), 2.0) ); 00915 dotSizes[i] = dotOrgDSize * dist; 00916 } 00917 // else just keep size 00918 } 00919 00920 // move the dots if needed 00921 if(haveMotion) 00922 for(uint i = 0; i < DOT_NUM; i++) 00923 { 00924 dots[i] = Point2D<float> 00925 (dots[i].i + DOT_VEL * (dots[i].i - FOE_X), 00926 dots[i].j + DOT_VEL * (dots[i].j - FOE_Y)); 00927 } 00928 00929 // add lateral motion 00930 if(dx != 0.0 || dy != 0.0) 00931 for(uint i = 0; i < DOT_NUM; i++) 00932 { 00933 dots[i] = Point2D<float>(dots[i].i + dx, dots[i].j + dy); 00934 } 00935 00936 // finally draw the dots 00937 for(uint i = 0; i < DOT_NUM; i++) 00938 { 00939 //LINFO("loc: %10.3f %10.3f: size: %7.3f", 00940 // dots[i].i, dots[i].j, dotSizes[i]); 00941 drawDisk(temp,Point2D<int>(dots[i].i, dots[i].j),dotSizes[i],byte(255)); 00942 //temp.setVal(dots[i].i, dots[i].j, byte(128)); 00943 } 00944 00945 // draw the FOE 00946 //drawDisk(temp,Point2D<int>(FOE_X,FOE_Y),2,byte(128)); 00947 00948 return temp; 00949 } 00950 00951 00952 // ###################################################################### 00953 // this is stimuli like in Fukuchi,Tsuchiya,Koch VSS/JOV 2009/2010 00954 // FOE is FOE_X,FOE_Y 00955 Image<byte> orgImage; 00956 Image<byte> getCleanFOE 00957 (uint step, uint totalStep, uint mag, float dx, float dy, Image<byte> image) 00958 { 00959 // just loop it 00960 step = step % totalStep; 00961 00962 // set original image on first step 00963 if(step == 0) 00964 { 00965 orgImage = image; 00966 return image; 00967 } 00968 image = orgImage; 00969 00970 uint width = image.getWidth(); 00971 uint height = image.getHeight(); 00972 00973 float nsize = (step/(totalStep - 1.0)); 00974 float scale = 1.0 / (1.0 - nsize*1.0/mag); 00975 Image<byte> temp = rescale(image, scale*width, scale*height); 00976 float nwidth = temp.getWidth(); 00977 float nheight = temp.getHeight(); 00978 00979 float px = float(FOE_X)/float(width); 00980 float py = float(FOE_Y)/float(height); 00981 00982 float foeX = px*nwidth; 00983 float foeY = py*nheight; 00984 00985 float left = foeX - float(FOE_X) + dx*step; 00986 float top = foeY - float(FOE_Y) + dy*step; 00987 00988 //if(top < 0.0) top = 0.0; 00989 //if(left < 0.0) left = 0.0; 00990 00991 Rectangle r = 00992 Rectangle::tlbrI(top, left, top+height-1, left+width-1); 00993 00994 LINFO("[%3d/%3d] FOE(%7.3f %7.3f) %f p(%7.3f %7.3f) [[%7.3f %7.3f]] " 00995 "[%3d %3d %3d %3d] temp(%3d %3d) ((%3d %3d))", 00996 step, totalStep, 00997 foeX,foeY, scale, px, py, top, left, 00998 r.top(), r.left(), r.bottomI(), r.rightI(), 00999 temp.getWidth(), temp.getHeight(), 01000 r.width(),r.height()); 01001 Image<byte> result = crop(temp, r); 01002 01003 return result; 01004 } 01005 01006 // ###################################################################### 01007 /* So things look consistent in everyone's emacs... */ 01008 /* Local Variables: */ 01009 /* indent-tabs-mode: nil */ 01010 /* End: */