00001 /*!@file Robots2/Beobot2/Navigation/FOE_Navigation/MiddleTemporal.C */ 00002 // //////////////////////////////////////////////////////////////////// // 00003 // The iLab Neuromorphic Vision C++ Toolkit - Copyright (C) 2001 by the // 00004 // University of Southern California (USC) and the iLab at USC. // 00005 // See http://iLab.usc.edu for information about this project. // 00006 // //////////////////////////////////////////////////////////////////// // 00007 // Major portions of the iLab Neuromorphic Vision Toolkit are protected // 00008 // under the U.S. patent ``Computation of Intrinsic Perceptual Saliency // 00009 // in Visual Environments, and Applications'' by Christof Koch and // 00010 // Laurent Itti, California Institute of Technology, 2001 (patent // 00011 // pending; application number 09/912,225 filed July 23, 2001; see // 00012 // http://pair.uspto.gov/cgi-bin/final/home.pl for current status). // 00013 // //////////////////////////////////////////////////////////////////// // 00014 // This file is part of the iLab Neuromorphic Vision C++ Toolkit. // 00015 // // 00016 // The iLab Neuromorphic Vision C++ Toolkit is free software; you can // 00017 // redistribute it and/or modify it under the terms of the GNU General // 00018 // Public License as published by the Free Software Foundation; either // 00019 // version 2 of the License, or (at your option) any later version. // 00020 // // 00021 // The iLab Neuromorphic Vision C++ Toolkit is distributed in the hope // 00022 // that it will be useful, but WITHOUT ANY WARRANTY; without even the // 00023 // implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR // 00024 // PURPOSE. See the GNU General Public License for more details. // 00025 // // 00026 // You should have received a copy of the GNU General Public License // 00027 // along with the iLab Neuromorphic Vision C++ Toolkit; if not, write // 00028 // to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, // 00029 // Boston, MA 02111-1307 USA. // 00030 // //////////////////////////////////////////////////////////////////// // 00031 // 00032 // Primary maintainer for this file: Christian Siagian <siagian@usc.edu> 00033 // $HeadURL: svn://ilab.usc.edu/trunk/saliency/src/Robots/Beobot2/Navigation/FOE_Navigation/MiddleTemporal.C 00034 // $ $Id: $ 00035 // 00036 ////////////////////////////////////////////////////////////////////////// 00037 00038 #include <stdio.h> 00039 #include <stdlib.h> 00040 00041 #include "Robots/Beobot2/Navigation/FOE_Navigation/MiddleTemporal.H" 00042 00043 #include <cstdio> 00044 #include "Image/ColorOps.H" 00045 #include "Image/CutPaste.H" 00046 #include "Image/DrawOps.H" 00047 #include "Image/FilterOps.H" 00048 #include "Image/MathOps.H" 00049 #include "Image/Pixels.H" 00050 #include "Image/ShapeOps.H" 00051 #include "Image/SimpleFont.H" 00052 00053 #include "Util/Timer.H" 00054 00055 // ###################################################################### 00056 // ###################################################################### 00057 00058 // ###################################################################### 00059 MiddleTemporal::MiddleTemporal() 00060 { 00061 itsWin.reset(); 00062 } 00063 00064 // ###################################################################### 00065 MiddleTemporal::~MiddleTemporal() 00066 { } 00067 00068 // ###################################################################### 00069 std::vector<Image<float> > MiddleTemporal::getMTfeatures() 00070 { 00071 return itsMTfeatures; 00072 } 00073 00074 // ###################################################################### 00075 std::vector<Image<float> > MiddleTemporal::getMToptimalShift() 00076 { 00077 return itsMToptimalShift; 00078 } 00079 00080 // ###################################################################### 00081 void MiddleTemporal::computeMTfeatures 00082 (std::vector<std::vector<ImageSet<float> > > rawSpatioTemporalEnergy) 00083 { 00084 if(rawSpatioTemporalEnergy[0][0].size() == 0) return; 00085 itsRawSpatioTemporalEnergy = rawSpatioTemporalEnergy; 00086 reset(); 00087 00088 // find optimal speed for each direction 00089 //Timer tim(1000000); 00090 for (uint i = 0; i < itsNumDirs; i++) 00091 { 00092 //tim.reset(); 00093 computeMaxSpeed(i); 00094 //uint64 t1 = tim.get(); 00095 //LINFO("[%3d] max speed: %f", i, t1/1000.0); 00096 } 00097 00098 //displayItsSpatioTemporalEnergy(); 00099 //printItsSpatioTemporalEnergy(140, 158, 45, 85, true, 100.0); 00100 00101 // compute max in 4x4 top level regions 00102 computeSteMaxVals(); 00103 00104 // DEBUG 00105 // LINFO("Max Vals 0"); 00106 // print(itsSteMaxVals[0], 170/4, 188/4, 45/4, 75/4, true); 00107 // LINFO("MaxVals 1"); 00108 // print(itsSteMaxVals[1], 170/4, 188/4, 45/4, 75/4, true); 00109 00110 // normalize with the max values regardless of direction 00111 normalizeOnMaxVal(); 00112 00113 // DEBUG 00114 // LINFO("After Normalize on Max"); 00115 // printItsSpatioTemporalEnergy(170, 188, 45, 75, true); 00116 00117 // opponencies: NOTE: from opposite direction of motion 00118 // this will subdue locations that do not have maximum values 00119 computeDirSteMaxVals(); 00120 00121 // DEBUG 00122 // LINFO("0 Max Vals 0"); 00123 // print(itsDirSteMaxVals[0][0], 170/4, 188/4, 45/4, 75/4, true); 00124 // LINFO("0 MaxVals 1"); 00125 // print(itsDirSteMaxVals[0][1], 170/4, 188/4, 45/4, 75/4, true); 00126 00127 // LINFO("4 Max Vals 0"); 00128 // print(itsDirSteMaxVals[4][0], 170/4, 188/4, 45/4, 75/4, true); 00129 // LINFO("4 MaxVals 1"); 00130 // print(itsDirSteMaxVals[4][1], 170/4, 188/4, 45/4, 75/4, true); 00131 00132 computeOpponencies(); 00133 00134 // DEBUG 00135 // LINFO("after opponencies"); 00136 // printItsSpatioTemporalEnergy(170, 188, 45, 75, true); 00137 // //print(motion, 165/div, 175/div, 45/div, 75/div, true); 00138 // //print(motion, 175/div, 185/div, 45/div, 75/div, true); 00139 // //print(motion, 185/div, 195/div, 45/div, 75/div, true); 00140 00141 00142 // normalize the MT features with the percentage of dominance 00143 weighMTfeaturesForDominance(); 00144 00145 // find max values for each direction among all the scales 00146 findMaxMotionVals(); 00147 00148 // DEBUG 00149 //printItsSpatioTemporalEnergy(170, 188, 45, 75, true, 100.0); 00150 00151 // DEBUG 00152 // for ACB: 5,15,30,50 00153 //printItsMTfeatures(35, 50, 5, 25, true); 00154 //printItsMToptimalShift(35, 50, 5, 25, true); 00155 //displayItsMTfeatures(); 00156 //displayItsMToptimalShift(); 00157 } 00158 00159 // ###################################################################### 00160 void MiddleTemporal::reset() 00161 { 00162 itsNumDirs = itsRawSpatioTemporalEnergy.size(); 00163 itsNumSpeeds = itsRawSpatioTemporalEnergy[0].size(); 00164 itsNumPyrLevels = itsRawSpatioTemporalEnergy[0][0].size(); 00165 00166 //itsSpatioTemporalPyrs.clear(); 00167 //itsSpatioTemporalPyrs.resize(itsNumDirs); 00168 //for(uint i = 0; i < itsNumDirs; i++) 00169 // itsSpatioTemporalPyrs[i].resize(itsNumSpeeds); 00170 00171 //itsRawSpatioTemporalEnergy.clear(); 00172 //itsRawSpatioTemporalEnergy.resize(itsNumDirs); 00173 //for(uint i = 0; i < itsNumDirs; i++) 00174 // itsRawSpatioTemporalEnergy[i].resize(itsNumSpeeds); 00175 00176 itsSpatioTemporalEnergy.resize(itsNumDirs); 00177 itsSpatioTemporalEnergyOptimalShift.resize(itsNumDirs); 00178 for(uint i = 0; i < itsNumDirs; i++) 00179 itsSpatioTemporalEnergyOptimalShift[i].reset(itsNumPyrLevels); 00180 00181 itsMTfeatures.resize(itsNumDirs); 00182 itsMToptimalShift.resize(itsNumDirs); 00183 } 00184 00185 00186 // ###################################################################### 00187 void MiddleTemporal::computeMaxSpeed(uint index) 00188 { 00189 int depth = itsRawSpatioTemporalEnergy[index][0].size(); 00190 if(depth == 0) return; 00191 ImageSet<float> result(depth); 00192 00193 // compute the motion detection at each location 00194 for (int scale = 0; scale < depth; scale++) 00195 result[scale] = computeMaxSpeed(index, scale); 00196 00197 itsSpatioTemporalEnergy[index] = result; 00198 } 00199 00200 // ###################################################################### 00201 Image<float> MiddleTemporal::computeMaxSpeed(uint index, int scale) 00202 { 00203 // find max among all the spatial and temporal shifts 00204 // --> basically max among all the shifting values 00205 00206 uint width = itsRawSpatioTemporalEnergy[index][0][scale].getWidth(); 00207 uint height = itsRawSpatioTemporalEnergy[index][0][scale].getHeight(); 00208 Image<float> result(width, height, ZEROS); 00209 00210 itsSpatioTemporalEnergyOptimalShift[index][scale] = 00211 Image<float>(width, height, ZEROS); 00212 00213 Image<float>::iterator resultT = result.beginw(); 00214 00215 Image<float>::iterator 00216 shiftT = itsSpatioTemporalEnergyOptimalShift[index][scale].beginw(); 00217 00218 Image<float>::iterator resT[itsNumSpeeds]; 00219 for (unsigned int i = 0; i < itsNumSpeeds; i++) 00220 resT[i] = itsRawSpatioTemporalEnergy[index][i][scale].beginw(); 00221 00222 for(uint i = 0; i < width; i++) 00223 { 00224 for(uint j = 0; j < height; j++) 00225 { 00226 float max = 0.0; float optShift = 0.0; 00227 for(uint r = 0; r < itsNumSpeeds; r++) 00228 { 00229 float val = *resT[r]++; 00230 if(max < val) 00231 { 00232 max = val; 00233 optShift = pow(2.0,r); //FIXXX add case for 1/2, 1/3, 1/4 00234 } 00235 } 00236 *resultT++ = max; 00237 *shiftT++ = optShift; 00238 } 00239 } 00240 00241 return result; 00242 } 00243 00244 // ###################################################################### 00245 void MiddleTemporal::computeSteMaxVals() 00246 { 00247 itsSteMaxVals.reset(itsNumPyrLevels); 00248 //itsDirSteMaxVals.resize(itsNumDirs); 00249 //for(uint i = 0; i < itsNumDirs; i++) 00250 // itsDirSteMaxVals[i].reset(itsNumPyrLevels); 00251 00252 uint width = itsSpatioTemporalEnergy[0][0].getWidth(); 00253 uint height = itsSpatioTemporalEnergy[0][0].getHeight(); 00254 00255 // got through each level 00256 for(uint l = 0; l < itsNumPyrLevels; l++) 00257 { 00258 uint pxSize = uint(pow(2.0, l)); 00259 uint lwidth = width/pxSize; 00260 uint lheight = height/pxSize; 00261 00262 // create a local max map 00263 // NOTE: is global max biologically plausible? 00264 uint steps = 1; 00265 if(pxSize < MAX_NEIGHBORHOOD) 00266 steps = MAX_NEIGHBORHOOD/pxSize; 00267 00268 // fill in max values for each level 00269 itsSteMaxVals[l] = Image<float>(lwidth/steps, lheight/steps, NO_INIT); 00270 //for(uint dir = 0; dir < itsNumDirs; dir++) 00271 // itsDirSteMaxVals[dir][l] = 00272 // Image<float>(lwidth/steps, lheight/steps, NO_INIT); 00273 00274 //LINFO("[%3d] lw/h: %d %d -> %d step: %d", 00275 // l, lwidth, lheight, pxSize, steps); 00276 00277 for(uint i = 0; i < lwidth; i+= steps) 00278 { 00279 for(uint j = 0; j < lheight; j+= steps) 00280 { 00281 // we find max for all directions 00282 float max = 0.0; 00283 for(uint d = 0; d < itsNumDirs; d++) 00284 { 00285 //float dirMax = 0.0; 00286 for(uint di = 0; di < steps; di++) 00287 { 00288 for(uint dj = 0; dj < steps; dj++) 00289 { 00290 float val = 00291 itsSpatioTemporalEnergy[d][l].getVal(i+di,j+dj); 00292 if(val > max) max = val; 00293 //if(val > dirMax) dirMax = val; 00294 00295 00296 // // DEBUG: 00297 // if(i >= 148 && i <= 151 && 00298 // j >= 60 && j <= 63 ) 00299 // { 00300 // LINFO("[%d](%3d %3d) d(%3d %3d) -> %3d %3d: %f %f", 00301 // d,i,j, di,dj, i+di,j+dj, val, max); 00302 // } 00303 00304 00305 00306 00307 } 00308 } 00309 //itsDirSteMaxVals[d][l].setVal(i/steps, j/steps, dirMax); 00310 } 00311 itsSteMaxVals[l].setVal(i/steps, j/steps, max); 00312 } 00313 } 00314 } 00315 } 00316 00317 // ###################################################################### 00318 void MiddleTemporal::computeDirSteMaxVals() 00319 { 00320 //itsSteMaxVals.reset(itsNumPyrLevels); 00321 itsDirSteMaxVals.resize(itsNumDirs); 00322 for(uint i = 0; i < itsNumDirs; i++) 00323 itsDirSteMaxVals[i].reset(itsNumPyrLevels); 00324 00325 uint width = itsSpatioTemporalEnergy[0][0].getWidth(); 00326 uint height = itsSpatioTemporalEnergy[0][0].getHeight(); 00327 00328 // got through each level 00329 for(uint l = 0; l < itsNumPyrLevels; l++) 00330 { 00331 uint pxSize = uint(pow(2.0, l)); 00332 uint lwidth = width/pxSize; 00333 uint lheight = height/pxSize; 00334 00335 // create a local max map 00336 // NOTE: is global max biologically plausible? 00337 uint steps = 1; 00338 if(pxSize < MAX_NEIGHBORHOOD) 00339 steps = MAX_NEIGHBORHOOD/pxSize; 00340 00341 // fill in max values for each level 00342 //itsSteMaxVals[l] = Image<float>(lwidth/steps, lheight/steps, NO_INIT); 00343 for(uint dir = 0; dir < itsNumDirs; dir++) 00344 itsDirSteMaxVals[dir][l] = 00345 Image<float>(lwidth/steps, lheight/steps, NO_INIT); 00346 00347 //LINFO("[%3d] lw/h: %d %d -> %d step: %d", 00348 // l, lwidth, lheight, pxSize, steps); 00349 00350 for(uint i = 0; i < lwidth; i+= steps) 00351 { 00352 for(uint j = 0; j < lheight; j+= steps) 00353 { 00354 // we find max for all directions 00355 //float max = 0.0; 00356 for(uint d = 0; d < itsNumDirs; d++) 00357 { 00358 float dirMax = 0.0; 00359 for(uint di = 0; di < steps; di++) 00360 { 00361 for(uint dj = 0; dj < steps; dj++) 00362 { 00363 float val = 00364 itsSpatioTemporalEnergy[d][l].getVal(i+di,j+dj); 00365 //if(val > max) max = val; 00366 if(val > dirMax) dirMax = val; 00367 00368 00369 // // DEBUG: 00370 // if(i >= 148 && i <= 151 && 00371 // j >= 60 && j <= 63 ) 00372 // { 00373 // LINFO("[%d](%3d %3d) d(%3d %3d) -> %3d %3d: %f %f", 00374 // d,i,j, di,dj, i+di,j+dj, val, max); 00375 // } 00376 00377 00378 00379 00380 } 00381 } 00382 itsDirSteMaxVals[d][l].setVal(i/steps, j/steps, dirMax); 00383 } 00384 //itsSteMaxVals[l].setVal(i/steps, j/steps, max); 00385 } 00386 } 00387 } 00388 } 00389 00390 // ###################################################################### 00391 // normalize on the max value regardless of direction 00392 void MiddleTemporal::normalizeOnMaxVal() 00393 { 00394 uint width = itsSpatioTemporalEnergy[0][0].getWidth(); 00395 uint height = itsSpatioTemporalEnergy[0][0].getHeight(); 00396 00397 // got through each level 00398 for(uint l = 0; l < itsNumPyrLevels; l++) 00399 { 00400 uint pxSize = uint(pow(2.0, l)); 00401 uint lwidth = width/pxSize; 00402 uint lheight = height/pxSize; 00403 00404 uint steps = 1; 00405 if(pxSize < MAX_NEIGHBORHOOD) steps = MAX_NEIGHBORHOOD/pxSize; 00406 for(uint i = 0; i < lwidth; i+= steps) 00407 { 00408 for(uint j = 0; j < lheight; j+= steps) 00409 { 00410 uint mi = i/steps; 00411 uint mj = j/steps; 00412 00413 uint lm = 0; if(mi > 2) lm = mi - 3; 00414 uint rm = lwidth/steps-1; if(mi < lwidth/steps-4) rm = mi + 3; 00415 uint tm = 0; if(mj > 2) tm = mj - 3; 00416 uint bm = lheight/steps-1; if(mj < lheight/steps-4)bm = mj + 3; 00417 00418 // FIXXX: need to interpolate for levels 3 and higher 00419 00420 // // DEBUG: 00421 // if(i >= 148 && i <= 151 && 00422 // j >= 60 && j <= 63 ) 00423 // { 00424 // LINFO("(%3d %3d %3d %3d) -> %3d %3d", 00425 // lm,rm,tm,bm, mi,mj); 00426 // } 00427 00428 00429 // get max vals among a neighborhood 00430 float max = 0.0; 00431 for(uint bi = lm; bi <= rm; bi++) 00432 { 00433 for(uint bj = tm; bj <= bm; bj++) 00434 { 00435 float val = itsSteMaxVals[l].getVal(bi, bj); 00436 if(max < val) max = val; 00437 00438 00439 // // DEBUG: 00440 // if(i >= 148 && i <= 151 && 00441 // j >= 60 && j <= 63 ) 00442 // { 00443 // LINFO("(%3d %3d) val %10.3f -> max %10.3f", 00444 // bi, bj, val, max); 00445 // } 00446 00447 00448 00449 00450 } 00451 } 00452 00453 // normalize each point 00454 for(uint d = 0; d < itsNumDirs; d++) 00455 { 00456 for(uint di = 0; di < steps; di++) 00457 { 00458 for(uint dj = 0; dj < steps; dj++) 00459 { 00460 float val = itsSpatioTemporalEnergy[d][l].getVal(i+di,j+dj); 00461 00462 if(max == 0.0) 00463 itsSpatioTemporalEnergy[d][l].setVal(i+di,j+dj, 0.0); 00464 else 00465 itsSpatioTemporalEnergy[d][l].setVal(i+di,j+dj, val/max); 00466 00467 00468 00469 // if(val > 30000.0) 00470 // LINFO("[%d](%3d %3d) d(%3d %3d) -> %3d %3d: %f/%f = %f", 00471 // d,i,j, di,dj, i+di,j+dj, val, max, val/max); 00472 00473 // // DEBUG: 00474 // if(i+di >= 148 && i+di <= 151 && 00475 // j+dj >= 60 && j+dj <= 63 ) 00476 // { 00477 // LINFO("[%d](%3d %3d) d(%3d %3d) -> %3d %3d: %f/%f = %f", 00478 // d,i,j, di,dj, i+di,j+dj, val, max, val/max); 00479 // } 00480 00481 00482 00483 00484 } 00485 } 00486 } 00487 } 00488 } 00489 } 00490 } 00491 00492 // ###################################################################### 00493 void MiddleTemporal::computeOpponencies() 00494 { 00495 // get lateral inhibition values on each location 00496 std::vector <ImageSet<float> > osInhibitMap(itsNumDirs); 00497 std::vector <ImageSet<float> > nInhibitMap(itsNumDirs); 00498 for (uint i = 0; i < itsNumDirs; i++) 00499 { 00500 osInhibitMap[i].reset(itsSpatioTemporalEnergy[i].size()); 00501 //nInhibitMap[i].reset(itsSpatioTemporalEnergy[i].size()); 00502 for(uint j = 0; j < itsSpatioTemporalEnergy[i].size(); j++) 00503 { 00504 osInhibitMap[i][j] = getOnSpotInhibitSpatioTemporalEnergy(i,j); 00505 //nInhibitMap[i][j] = getNeighborInhibitSpatioTemporalEnergy(i,j); 00506 } 00507 } 00508 00509 // perform lateral inhibition on each location 00510 for (uint i = 0; i < itsNumDirs; i++) 00511 { 00512 for(uint j = 0; j < itsSpatioTemporalEnergy[i].size(); j++) 00513 { 00514 itsSpatioTemporalEnergy[i][j] -= osInhibitMap[i][j]; 00515 00516 // for (uint k = 0; k < itsNumDirs; k++) 00517 // { 00518 // // NOTE FIX maybe add weights 00519 // // according to how disagreeing the direction is 00520 // if(k != i) 00521 // itsSpatioTemporalEnergy[i][j] -= nInhibitMap[i][j]; 00522 // } 00523 00524 // we are going to clamp the result to 0.0; 00525 float mn,mx; getMinMax(itsSpatioTemporalEnergy[i][j],mn,mx); 00526 inplaceClamp(itsSpatioTemporalEnergy[i][j], 0.0f, mx); 00527 } 00528 } 00529 00530 // NOTE: ---> how about end-stop neurons for end of bars 00531 00532 // at this point everything should have been normalized to 0 to 1.0 max 00533 } 00534 00535 // ###################################################################### 00536 void MiddleTemporal::weighMTfeaturesForDominance() 00537 { 00538 00539 } 00540 00541 // ###################################################################### 00542 Image<float> MiddleTemporal::getOnSpotInhibitSpatioTemporalEnergy 00543 (uint dir, uint scale) 00544 { 00545 uint oppDir = (dir + itsNumDirs/2)%itsNumDirs; 00546 00547 uint width = itsSpatioTemporalEnergy[dir][scale].getWidth(); 00548 uint height = itsSpatioTemporalEnergy[dir][scale].getHeight(); 00549 uint pxSize = uint(pow(2.0, scale)); 00550 uint steps = 1; 00551 if(pxSize < MAX_NEIGHBORHOOD) steps = MAX_NEIGHBORHOOD/pxSize; 00552 uint nwidth = width/steps; 00553 uint nheight = height/steps; 00554 00555 00556 Image<float> result(width,height,NO_INIT); 00557 Image<float>::iterator resultT = result.beginw(); 00558 00559 /*Image<float>::iterator stT[itsNumDirs]; 00560 for (unsigned int i=0; i < itsNumDirs; i++) 00561 stT[i] = itsSpatioTemporalEnergy[i][scale].beginw();*/ 00562 00563 // NOTE: (other dir average then subtract) have more artifacts 00564 // but keep all other possible evidences 00565 // may help in aperture problem 00566 // DO NOT just get the max (although it is cleaner) 00567 for(uint j = 0; j < height; j++) 00568 { 00569 for(uint i = 0; i < width; i++) 00570 { 00571 float sum = 0.0; 00572 float opp = 0.0; 00573 for (uint k = 0; k < itsNumDirs; k++) 00574 { 00575 // NOTE: use this line if don't want center surround 00576 //float tval = *stT[k]++; 00577 00578 // NOTE: to find 4x4 neighborhood max values of opponencies 00579 // better than just a single location 00580 // get the max vals around this location 00581 uint mi = i/steps; 00582 uint mj = j/steps; 00583 00584 uint lm = 0; if(mi > 0) lm = mi - 1; 00585 uint rm = nwidth-1; if(mi < nwidth-2) rm = mi + 1; 00586 uint tm = 0; if(mj > 0) tm = mj - 1; 00587 uint bm = nheight-1; if(mj < nheight-2)bm = mj + 1; 00588 00589 // // DEBUG: 00590 // if(i == 180 && j == 60) 00591 // if(i >= 148 && i <= 151 && j >= 60 && j <= 63 ) 00592 // { 00593 // LINFO("[%3d %3d] (%3d %3d %3d %3d) -> %3d %3d", i,j, lm,rm,tm,bm, mi,mj); 00594 // } 00595 00596 float nmax = 0.0; 00597 for(uint bi = lm; bi <= rm; bi++) 00598 { 00599 for(uint bj = tm; bj <= bm; bj++) 00600 { 00601 float nval = itsDirSteMaxVals[k][scale].getVal(bi, bj); 00602 if(nmax < nval) nmax = nval; 00603 00604 //if(i == 180 && j == 60) 00605 // if(i >= 148 && i <= 151 && j >= 60 && j <= 63 ) 00606 //{ 00607 // LINFO("[%3d %3d] -> %10.3f %10.3f", bi,bj, nval, nmax); 00608 //} 00609 00610 00611 00612 } 00613 } 00614 00615 float tval = nmax; 00616 00617 00618 // accumulate opponencies only for other directions 00619 if(k != dir) sum += tval; 00620 //if(k != dir && k != oppDir) sum += tval; 00621 00622 // also find 00623 if(k == oppDir) opp = tval; 00624 //if(max < tval) max = tval; 00625 00626 } 00627 sum /= (itsNumDirs-2.0); 00628 00629 00630 // NOTE: experiment with both average and just the opposite direction 00631 // -->on stationary stimuli (or stimuli that's too slow/fast) 00632 // opposite direction gives equal firings. 00633 *resultT++ = opp; //(sum+opp); 00634 } 00635 } 00636 00637 return result; 00638 } 00639 00640 // ###################################################################### 00641 Image<float> MiddleTemporal::getNeighborInhibitSpatioTemporalEnergy 00642 (uint dir, uint scale) 00643 { 00644 uint width = itsSpatioTemporalEnergy[dir][scale].getWidth(); 00645 uint height = itsSpatioTemporalEnergy[dir][scale].getHeight(); 00646 Image<float> result(width,height,ZEROS); 00647 00648 float cosAng = cos((2.0*M_PI * dir)/itsNumDirs); 00649 float sinAng = sin((2.0*M_PI * dir)/itsNumDirs); 00650 00651 Image<float>::iterator stT[itsNumDirs]; 00652 for (unsigned int i=0; i < itsNumDirs; i++) 00653 stT[i] = itsSpatioTemporalEnergy[i][scale].beginw(); 00654 00655 // NOTE: FIX maybe add .5 to make sure the diagonal is taken care 00656 //LINFO("c,s(%10.3f, %10.3f) [%3d %3d]", cosAng, sinAng, width, height); 00657 00658 // NOTE: inhibit neighbor motion if they're not in agreement with yours 00659 00660 std::vector<float> fweights(4); 00661 fweights[0] = .15; 00662 fweights[1] = .07; 00663 fweights[2] = .02; 00664 fweights[3] = .01; 00665 00666 std::vector<float> bweights(3); 00667 bweights[0] = .10; 00668 bweights[1] = .05; 00669 bweights[2] = .02; 00670 00671 for(uint j = 0; j < height; j++) 00672 { 00673 for(uint i = 0; i < width; i++) 00674 { 00675 float val = *stT[dir]++; 00676 //float val = itsSpatioTemporalEnergy[dir][scale].getVal(i,j); 00677 for(uint k = 0; k < fweights.size(); k++) 00678 { 00679 int ii = (i + (k+1.0)*cosAng); 00680 int jj = (j + (k+1.0)*sinAng); 00681 00682 if(ii > 0 && ii < int(width) && 00683 jj > 0 && jj < int(height) ) 00684 { 00685 float rval = result.getVal(ii,jj); 00686 result.setVal(ii,jj, rval + (fweights[k] * val)); 00687 } 00688 } 00689 } 00690 00691 // add the backward inhibitory as well if needed 00692 } 00693 return result; 00694 } 00695 00696 // ###################################################################### 00697 void MiddleTemporal::findMaxMotionVals() 00698 { 00699 // SCALE INTEGRATION 00700 // normalizing to 1.0 is working if we expand the local max normalizer 00701 // we only use 2 levels now to go from 1 pix - 8pix displacement 00702 // all that it needed really 00703 00704 uint width = itsSpatioTemporalEnergy[0][0].getWidth(); 00705 uint height = itsSpatioTemporalEnergy[0][0].getHeight(); 00706 00707 uint mWidth = width/MAX_NEIGHBORHOOD; 00708 uint mHeight = height/MAX_NEIGHBORHOOD; 00709 //LINFO("MTfeatures size: %d x %d", mWidth, mHeight); 00710 00711 // got through each point in each direction 00712 for(uint d = 0; d < itsNumDirs; d++) 00713 { 00714 Image<float> tMTfeatures (mWidth, mHeight, NO_INIT); 00715 Image<float>::iterator tmtT = tMTfeatures.beginw(); 00716 00717 Image<float> tMToptimalShift (mWidth, mHeight, NO_INIT); 00718 Image<float>::iterator tmtosT = tMToptimalShift.beginw(); 00719 00720 for(uint j = 0; j < mHeight; j++) 00721 { 00722 for(uint i = 0; i < mWidth; i++) 00723 { 00724 // find max among all scales 00725 float max = 0.0; float optShift = 0.0; 00726 for(uint l = 0; l < itsNumPyrLevels; l++) 00727 { 00728 uint pxSize = uint(pow(2.0, l)); 00729 uint lwidth = width/pxSize; 00730 uint lheight = height/pxSize; 00731 00732 uint steps = 1; 00733 if(pxSize < MAX_NEIGHBORHOOD) 00734 steps = MAX_NEIGHBORHOOD/pxSize; 00735 00736 // translated coordinate 00737 uint ti = i * steps; 00738 uint tj = j * steps; 00739 00740 // take care of boundary conditions 00741 uint lstep = ti; if(ti >= steps) lstep = ti - steps; 00742 uint rstep = ti; if(ti+steps < lwidth) rstep = ti + steps; 00743 uint tstep = tj; if(tj >= steps) tstep = tj - steps; 00744 uint bstep = tj; if(tj+steps < lheight) bstep = tj + steps; 00745 00746 // this needs interpolation 00747 if(pxSize > MAX_NEIGHBORHOOD) 00748 { 00749 // NOTE: ADD LATER 00750 uint div = pxSize/MAX_NEIGHBORHOOD; 00751 //LINFO("interp: (%3d %3d) t[%3d %3d] {%3d %3d}: %3d %3d", 00752 // i,j, ti, tj, d, l, i/div, j/div); 00753 float val = 00754 itsSpatioTemporalEnergy[d][l].getVal(i/div,j/div); 00755 float shift = 00756 itsSpatioTemporalEnergyOptimalShift[d][l].getVal(i/div,j/div); 00757 if(max < val) 00758 { 00759 max = val; 00760 optShift = shift; 00761 } 00762 } 00763 else 00764 { 00765 for(uint di = lstep; di < rstep; di++) 00766 { 00767 for(uint dj = tstep; dj < bstep; dj++) 00768 { 00769 //LINFO("(%3d %3d) t[%3d %3d] {%3d %3d}[%3d %3d %3d %3d] d: %3d %3d", 00770 // i,j, ti, tj, d, l, lstep, rstep, tstep, bstep, di, dj); 00771 float val = 00772 itsSpatioTemporalEnergy[d][l].getVal(di, dj); 00773 float shift = 00774 itsSpatioTemporalEnergyOptimalShift[d][l].getVal(di, dj) * pxSize; 00775 if(max < val) 00776 { 00777 max = val; 00778 optShift = shift; 00779 } 00780 } 00781 } 00782 } 00783 } 00784 00785 //tMTfeatures.setVal(i,j, max); 00786 //LINFO("%f %f %f",tMTfeatures.getVal(i,j), max, *tmtT); tmtT++; 00787 *tmtT++ = max; 00788 *tmtosT++ = optShift; 00789 } 00790 } 00791 itsMTfeatures[d] = tMTfeatures; 00792 itsMToptimalShift[d] = tMToptimalShift; 00793 } 00794 } 00795 00796 // ###################################################################### 00797 void MiddleTemporal::printItsSpatioTemporalEnergy 00798 (uint si, uint ei, uint sj, uint ej, bool stop, float div) 00799 { 00800 if(itsSpatioTemporalEnergy[0].size() == 0) return; 00801 00802 //for(uint k = 0; k < 1; k++) 00803 for(uint k = 0; k < itsNumPyrLevels; k++) 00804 { 00805 uint step = uint(pow(2.0,k)); 00806 for(uint i = 0; i < itsNumDirs; i++) 00807 { 00808 LINFO("itsSpatioTemporalEnergy features: dir: %d lev: %d", i, k); 00809 00810 for(uint y = sj/step; y < ej/step; y++) 00811 { 00812 for(uint x = si/step; x < ei/step; x++) 00813 printf("%8.3f ", itsSpatioTemporalEnergy[i][k].getVal(x,y)/div); 00814 printf("\n"); 00815 } 00816 LINFO("\n"); 00817 00818 //i+= 3; // < just to see dir: 0 and 4 or motion left and right 00819 00820 if(stop) Raster::waitForKey(); 00821 } 00822 } 00823 } 00824 00825 // ###################################################################### 00826 void MiddleTemporal::displayItsSpatioTemporalEnergy() 00827 { 00828 if(itsSpatioTemporalEnergy[0].size() == 0) return; 00829 uint w = itsSpatioTemporalEnergy[0][0].getWidth(); 00830 uint h = itsSpatioTemporalEnergy[0][0].getHeight(); 00831 Image<float> disp(w*4, h*2,NO_INIT); 00832 00833 if(itsWin.is_invalid()) 00834 itsWin.reset(new XWinManaged(Dims(w*4,h*2), w+10, 0, "FOE Detector")); 00835 else itsWin->setDims(Dims(w*4,h*2)); 00836 00837 for(uint i = 0; i < itsNumPyrLevels; i++) 00838 //for(uint i = 0; i < 1; i++) 00839 { 00840 LINFO("level: %d", i); 00841 00842 uint scale = uint(pow(2.0, i)); 00843 inplacePaste(disp, zoomXY(itsSpatioTemporalEnergy[0][i], scale), 00844 Point2D<int>(0, 0)); 00845 inplacePaste(disp, zoomXY(itsSpatioTemporalEnergy[1][i], scale), 00846 Point2D<int>(w, 0)); 00847 inplacePaste(disp, zoomXY(itsSpatioTemporalEnergy[2][i], scale), 00848 Point2D<int>(2*w, 0)); 00849 inplacePaste(disp, zoomXY(itsSpatioTemporalEnergy[3][i], scale), 00850 Point2D<int>(3*w, 0)); 00851 00852 if(itsNumDirs > 4) 00853 { 00854 inplacePaste(disp, zoomXY(itsSpatioTemporalEnergy[4][i], scale), 00855 Point2D<int>(0, h)); 00856 inplacePaste(disp, zoomXY(itsSpatioTemporalEnergy[5][i], scale), 00857 Point2D<int>(w, h)); 00858 inplacePaste(disp, zoomXY(itsSpatioTemporalEnergy[6][i], scale), 00859 Point2D<int>(2*w, h)); 00860 inplacePaste(disp, zoomXY(itsSpatioTemporalEnergy[7][i], scale), 00861 Point2D<int>(3*w, h)); 00862 } 00863 00864 if(itsNumDirs > 8) 00865 { 00866 inplacePaste(disp, zoomXY(itsSpatioTemporalEnergy[8][i], scale), 00867 Point2D<int>(0, h)); 00868 inplacePaste(disp, zoomXY(itsSpatioTemporalEnergy[9][i], scale), 00869 Point2D<int>(w/2, h)); 00870 inplacePaste(disp, zoomXY(itsSpatioTemporalEnergy[10][i], scale), 00871 Point2D<int>(w, h)); 00872 inplacePaste(disp, zoomXY(itsSpatioTemporalEnergy[11][i], scale), 00873 Point2D<int>(3*w/2, h)); 00874 } 00875 itsWin->drawImage(disp,0,0); 00876 Raster::waitForKey(); 00877 } 00878 } 00879 00880 // ###################################################################### 00881 void MiddleTemporal::printItsMTfeatures 00882 (uint si, uint ei, uint sj, uint ej, bool stop) 00883 { 00884 if(itsSpatioTemporalEnergy[0].size() == 0) return; 00885 00886 00887 LINFO("MT features "); 00888 for(uint i = 0; i < itsNumDirs; i++) 00889 { 00890 for(uint y = sj; y < ej; y++) 00891 { 00892 for(uint x = si; x < ei; x++) 00893 printf("%7.3f ", itsMTfeatures[i].getVal(x,y)); 00894 printf("\n"); 00895 } 00896 LINFO("MT features: %d", i); 00897 if(stop)Raster::waitForKey(); 00898 } 00899 } 00900 00901 // ###################################################################### 00902 void MiddleTemporal::printItsMToptimalShift 00903 (uint si, uint ei, uint sj, uint ej, bool stop) 00904 { 00905 if(itsSpatioTemporalEnergy[0].size() == 0) return; 00906 00907 00908 LINFO("MT features Optimal Shift"); 00909 for(uint i = 0; i < itsNumDirs; i++) 00910 { 00911 for(uint y = sj; y < ej; y++) 00912 { 00913 for(uint x = si; x < ei; x++) 00914 printf("%7.3f ", itsMToptimalShift[i].getVal(x,y)); 00915 printf("\n"); 00916 } 00917 LINFO("MT features: %d", i); 00918 if(stop)Raster::waitForKey(); 00919 } 00920 } 00921 00922 // ###################################################################### 00923 void MiddleTemporal::displayItsMToptimalShift() 00924 { 00925 if(itsSpatioTemporalEnergy[0].size() == 0) return; 00926 uint w = itsSpatioTemporalEnergy[0][0].getWidth(); 00927 uint h = itsSpatioTemporalEnergy[0][0].getHeight(); 00928 00929 uint wDisp = w*2; uint hDisp = h/2; 00930 if(itsNumDirs == 8) { wDisp = w*2; hDisp = h; } 00931 else if(itsNumDirs == 8) { wDisp = w*2; hDisp = 3*h/2; } 00932 00933 Image<float> disp(wDisp,hDisp,NO_INIT); 00934 if(itsWin.is_invalid()) 00935 itsWin.reset(new XWinManaged(Dims(wDisp,hDisp), w+10, 0, "FOE Detector")); 00936 else itsWin->setDims(Dims(wDisp, hDisp)); 00937 00938 LINFO("display MT optimal shift"); 00939 00940 inplacePaste(disp, zoomXY(itsMToptimalShift[0], MAX_NEIGHBORHOOD/2), 00941 Point2D<int>(0, 0)); 00942 inplacePaste(disp, zoomXY(itsMToptimalShift[1], MAX_NEIGHBORHOOD/2), 00943 Point2D<int>(w/2, 0)); 00944 inplacePaste(disp, zoomXY(itsMToptimalShift[2], MAX_NEIGHBORHOOD/2), 00945 Point2D<int>(w, 0)); 00946 inplacePaste(disp, zoomXY(itsMToptimalShift[3], MAX_NEIGHBORHOOD/2), 00947 Point2D<int>(3*w/2, 0)); 00948 00949 if(itsNumDirs > 4) 00950 { 00951 inplacePaste(disp, zoomXY(itsMToptimalShift[4], MAX_NEIGHBORHOOD/2), 00952 Point2D<int>(0, h/2)); 00953 inplacePaste(disp, zoomXY(itsMToptimalShift[5], MAX_NEIGHBORHOOD/2), 00954 Point2D<int>(w/2, h/2)); 00955 inplacePaste(disp, zoomXY(itsMToptimalShift[6], MAX_NEIGHBORHOOD/2), 00956 Point2D<int>(w, h/2)); 00957 inplacePaste(disp, zoomXY(itsMToptimalShift[7], MAX_NEIGHBORHOOD/2), 00958 Point2D<int>(3*w/2,h/2)); 00959 } 00960 00961 if(itsNumDirs > 8) 00962 { 00963 inplacePaste(disp, zoomXY(itsMToptimalShift[8], MAX_NEIGHBORHOOD/2), 00964 Point2D<int>(0, h)); 00965 inplacePaste(disp, zoomXY(itsMToptimalShift[9], MAX_NEIGHBORHOOD/2), 00966 Point2D<int>(w/2, h)); 00967 inplacePaste(disp, zoomXY(itsMToptimalShift[10], MAX_NEIGHBORHOOD/2), 00968 Point2D<int>(w, h)); 00969 inplacePaste(disp, zoomXY(itsMToptimalShift[11], MAX_NEIGHBORHOOD/2), 00970 Point2D<int>(3*w/2, h)); 00971 } 00972 00973 float mn,mx; getMinMax(disp,mn,mx); 00974 drawLine(disp, Point2D<int>(0 , h/2), Point2D<int>(w*2 , h/2), mx, 1); 00975 drawLine(disp, Point2D<int>(0 , h ), Point2D<int>(w*2 , h ), mx, 1); 00976 drawLine(disp, Point2D<int>(w/2 , 0 ), Point2D<int>(w/2 , h ), mx, 1); 00977 drawLine(disp, Point2D<int>(w , 0 ), Point2D<int>(w , h ), mx, 1); 00978 drawLine(disp, Point2D<int>(3*w/2, 0 ), Point2D<int>(3*w/2, h ), mx, 1); 00979 itsWin->drawImage(disp,0,0); 00980 Raster::waitForKey(); 00981 } 00982 00983 // ###################################################################### 00984 void MiddleTemporal::displayItsMTfeatures() 00985 { 00986 if(itsSpatioTemporalEnergy[0].size() == 0) return; 00987 uint w = itsSpatioTemporalEnergy[0][0].getWidth(); 00988 uint h = itsSpatioTemporalEnergy[0][0].getHeight(); 00989 00990 uint wDisp = w*2; uint hDisp = h/2; 00991 if(itsNumDirs == 8) { wDisp = w*2; hDisp = h; } 00992 else if(itsNumDirs == 8) { wDisp = w*2; hDisp = 3*h/2; } 00993 00994 Image<float> disp(wDisp,hDisp,NO_INIT); 00995 if(itsWin.is_invalid()) 00996 itsWin.reset(new XWinManaged(Dims(wDisp,hDisp), w+10, 0, "FOE Detector")); 00997 else itsWin->setDims(Dims(wDisp, hDisp)); 00998 00999 LINFO("display MT features"); 01000 01001 inplacePaste(disp, zoomXY(itsMTfeatures[0], MAX_NEIGHBORHOOD/2), 01002 Point2D<int>(0, 0)); 01003 inplacePaste(disp, zoomXY(itsMTfeatures[1], MAX_NEIGHBORHOOD/2), 01004 Point2D<int>(w/2, 0)); 01005 inplacePaste(disp, zoomXY(itsMTfeatures[2], MAX_NEIGHBORHOOD/2), 01006 Point2D<int>(w, 0)); 01007 inplacePaste(disp, zoomXY(itsMTfeatures[3], MAX_NEIGHBORHOOD/2), 01008 Point2D<int>(3*w/2, 0)); 01009 01010 if(itsNumDirs > 4) 01011 { 01012 inplacePaste(disp, zoomXY(itsMTfeatures[4], MAX_NEIGHBORHOOD/2), 01013 Point2D<int>(0, h/2)); 01014 inplacePaste(disp, zoomXY(itsMTfeatures[5], MAX_NEIGHBORHOOD/2), 01015 Point2D<int>(w/2, h/2)); 01016 inplacePaste(disp, zoomXY(itsMTfeatures[6], MAX_NEIGHBORHOOD/2), 01017 Point2D<int>(w, h/2)); 01018 inplacePaste(disp, zoomXY(itsMTfeatures[7], MAX_NEIGHBORHOOD/2), 01019 Point2D<int>(3*w/2,h/2)); 01020 } 01021 01022 if(itsNumDirs > 8) 01023 { 01024 inplacePaste(disp, zoomXY(itsMTfeatures[8], MAX_NEIGHBORHOOD/2), 01025 Point2D<int>(0, h)); 01026 inplacePaste(disp, zoomXY(itsMTfeatures[9], MAX_NEIGHBORHOOD/2), 01027 Point2D<int>(w/2, h)); 01028 inplacePaste(disp, zoomXY(itsMTfeatures[10], MAX_NEIGHBORHOOD/2), 01029 Point2D<int>(w, h)); 01030 inplacePaste(disp, zoomXY(itsMTfeatures[11], MAX_NEIGHBORHOOD/2), 01031 Point2D<int>(3*w/2, h)); 01032 } 01033 01034 float mn,mx; getMinMax(disp,mn,mx); 01035 drawLine(disp, Point2D<int>(0 , h/2), Point2D<int>(w*2 , h/2), mx, 1); 01036 drawLine(disp, Point2D<int>(0 , h ), Point2D<int>(w*2 , h ), mx, 1); 01037 drawLine(disp, Point2D<int>(w/2 , 0 ), Point2D<int>(w/2 , h ), mx, 1); 01038 drawLine(disp, Point2D<int>(w , 0 ), Point2D<int>(w , h ), mx, 1); 01039 drawLine(disp, Point2D<int>(3*w/2, 0 ), Point2D<int>(3*w/2, h ), mx, 1); 01040 itsWin->drawImage(disp,0,0); 01041 Raster::waitForKey(); 01042 } 01043 01044 // ###################################################################### 01045 Layout<byte> MiddleTemporal::getMTfeaturesDisplay(Image<byte> image) 01046 { 01047 if(itsRawSpatioTemporalEnergy.size() == 0 || 01048 itsRawSpatioTemporalEnergy[0][0].size() == 0) 01049 return Layout<byte>(image); 01050 01051 uint width = image.getWidth(); 01052 uint height = image.getHeight(); 01053 01054 uint scale = 1; 01055 Image<float> lum = image; 01056 Image<float> motion(width* scale, height * scale, ZEROS); 01057 01058 uint mtWidth = itsMTfeatures[0].getWidth(); 01059 uint mtHeight = itsMTfeatures[0].getHeight(); 01060 01061 Image<float>::iterator mtT[itsNumDirs]; 01062 for (unsigned int i=0; i < itsNumDirs; i++) 01063 mtT[i] = itsMTfeatures[i].beginw(); 01064 01065 Image<float>::iterator mtosT[itsNumDirs]; 01066 for (unsigned int i=0; i < itsNumDirs; i++) 01067 mtosT[i] = itsMToptimalShift[i].beginw(); 01068 01069 float boom = 0; 01070 // go through each MT locations 01071 for(uint dir = 0; dir < itsNumDirs; dir++) 01072 { 01073 float angle = (dir*2.0*M_PI)/itsNumDirs; 01074 if(dir%2 == 1) 01075 { 01076 //LINFO("[%d> ang: %f", dir, angle); 01077 angle = (dir*2.0*M_PI)/itsNumDirs + M_PI/2.0; 01078 //LINFO("<%d] ang: %f", dir, angle); 01079 } 01080 // draw an arrow for each location 01081 for(uint j = 0; j < mtHeight; j++) 01082 { 01083 for(uint i = 0; i < mtWidth; i++) 01084 { 01085 float val = *mtT[dir]++; 01086 float shift = *mtosT[dir]++; 01087 if(val > .20)// && dir == 3 && boom != 10 && shift == 8.0)//(dir != 0 && dir != 4)) 01088 { 01089 boom++; 01090 //LINFO("val %f, ang: %f (%d %d)", val, angle, i,j); 01091 drawLine(motion, 01092 Point2D<int>(i*MAX_NEIGHBORHOOD*scale, 01093 j*MAX_NEIGHBORHOOD*scale), 01094 //angle, val*MAX_NEIGHBORHOOD*scale, 1.0F); 01095 angle, shift*4*scale, val); 01096 01097 //drawDisk(motion, 01098 // Point2D<int>(i*MAX_NEIGHBORHOOD*scale, 01099 // j*MAX_NEIGHBORHOOD*scale), 2, val); 01100 } 01101 } 01102 } 01103 } 01104 01105 Image<float> res = motion; 01106 inplaceNormalize(res, 0.0F, 255.0F); 01107 inplaceNormalize(lum, 0.0F, 64.0F); 01108 inplaceNormalize(motion, 0.0F, 255.0F); 01109 01110 // draw the location of the FOE 01111 //drawCross(res, itsFoe, 1.0f, 10, 1); 01112 01113 Image<byte> vimg = res; 01114 //writeText(vimg, Point2D<int>(1,1), "img", byte(0), byte(255), 01115 // SimpleFont::fixedMaxWidth(8)); 01116 01117 Image<byte> himg = res; 01118 //writeText(himg, Point2D<int>(1,1), "motion", byte(0), byte(255), 01119 // SimpleFont::fixedMaxWidth(8)); 01120 01121 // Layout<byte> disp = 01122 // vcat(hcat(lum, Image<byte>(quad)), hcat(himg, vimg)); 01123 01124 Layout<byte> disp(res+lum); 01125 01126 return disp; 01127 } 01128 01129 // ###################################################################### 01130 /* So things look consistent in everyone's emacs... */ 01131 /* Local Variables: */ 01132 /* indent-tabs-mode: nil */ 01133 /* End: */