00001 /*!@file Robots/Beobot2/Navigation/FOE_Navigation/PopulationHeadingMap.C 00002 Lappe&Rauschecker 1993's Population Heading Map algorithm */ 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: svn://isvn.usc.edu/software/invt/trunk/saliency/src/Robots/Beobot2/Navigation/FOE_Navigation/PopulationHeadingMap.C 00035 // $Id: $ 00036 // 00037 #ifndef ROBOTS_BEOBOT2_NAVIGATION_FOENAVIGATION_POPULATIONHEADINGMAP_C_DEFINED 00038 #define ROBOTS_BEOBOT2_NAVIGATION_FOENAVIGATION_POPULATIONHEADINGMAP_C_DEFINED 00039 00040 #define PHM_NUM_DIRECTIONS 4 00041 #define PHM_FOE_STEP 16 00042 #define PHM_NUM_MSTD_POPULATION_NEURONS 20 00043 #define PHM_NUM_MSTD_INPUT_NEURONS 30 00044 00045 #define PHM_MU .20F 00046 00047 #include "Robots/Beobot2/Navigation/FOE_Navigation/PopulationHeadingMap.H" 00048 00049 #include "Image/CutPaste.H" 00050 #include "Image/DrawOps.H" 00051 #include "Image/MatrixOps.H" 00052 #include "Image/LinearAlgebra.H" 00053 #include "Image/ShapeOps.H" 00054 00055 #include "Util/Timer.H" 00056 00057 #include <stdio.h> 00058 00059 // ###################################################################### 00060 PopulationHeadingMap::PopulationHeadingMap(float focalLength): 00061 itsFocalLength(focalLength) 00062 { 00063 00064 } 00065 00066 // ###################################################################### 00067 void PopulationHeadingMap::setFocalLength(float focalLength) 00068 { 00069 itsFocalLength = focalLength; 00070 } 00071 00072 // ###################################################################### 00073 PopulationHeadingMap::~PopulationHeadingMap() 00074 { } 00075 00076 // ###################################################################### 00077 // it would be nice if this function runs fast 00078 // so that we can reinitialize 00079 void PopulationHeadingMap::initialize(Dims dims) 00080 { 00081 00082 // find the orthogonal complement of C(Tj) 00083 // find A: bases of C(Tj) 00084 // find the nullspace of A: this is the orthogonal complement 00085 00086 00087 00088 00089 // also the matched pair neuron 00090 // J_(i,j,k,l)' = - J_(i,j,k,l) 00091 00092 } 00093 00094 // ###################################################################### 00095 //! return FOE from image of movement between the two input images 00096 void PopulationHeadingMap::getFOE(Image<byte> stim1, Image<byte> stim2) 00097 { 00098 itsCurrentImage = stim1; 00099 getFOE(stim2); 00100 } 00101 00102 // ###################################################################### 00103 //! return FOE from image of movement 00104 //! between this image and the previous one 00105 void PopulationHeadingMap::getFOE(Image<byte> stim) 00106 { 00107 // NOTE: Particle filtering to run multiple hypotheses 00108 00109 // don't do anything if this is the first frame 00110 if(!itsCurrentImage.initialized()) 00111 { 00112 itsCurrentImage = stim; 00113 itsPreviousImage = stim; 00114 LINFO("NOT ENOUGH INPUT YET"); 00115 return; 00116 } 00117 00118 LINFO("Focal Length: %f", itsFocalLength); 00119 00120 itsPreviousImage = itsCurrentImage; 00121 itsCurrentImage = stim; 00122 00123 // compute the optic flow for the input MT neurons 00124 // : spatiotemporal, Lucas&Kanade, etc. 00125 itsOpticalFlow = 00126 getCleanOpticFlow(Image<byte>(itsPreviousImage.getDims(),ZEROS)); 00127 //getLucasKanadeOpticFlow(itsPreviousImage, itsCurrentImage); 00128 itsOpticalFlowImage = 00129 drawOpticFlow(itsPreviousImage, itsOpticalFlow); 00130 00131 //getCleanOpticFlow3(Image<byte>(itsPreviousImage.getDims(),ZEROS)); 00132 00133 if(itsWin.is_invalid()) 00134 itsWin.reset(new XWinManaged(itsCurrentImage.getDims(), 00135 0, 0, "Pop Heading Map")); 00136 itsWin->setDims(itsCurrentImage.getDims()); 00137 itsWin->drawImage(itsOpticalFlowImage,0,0); 00138 00139 LINFO("got the optic flow"); 00140 Raster::waitForKey(); 00141 00142 00143 //itsMTfeatures = itsOpticalFlow->getMTfeatures(); 00144 00145 // compute output for each MSTd neuron 00146 computePopulationHeading(); 00147 00148 // find the most likely heading 00149 00150 } 00151 // ###################################################################### 00152 void PopulationHeadingMap::computePopulationHeading() 00153 { 00154 itsHeadingDirectionMap = 00155 Image<float>(itsCurrentImage.getWidth() /PHM_FOE_STEP, 00156 itsCurrentImage.getHeight()/PHM_FOE_STEP, ZEROS); 00157 00158 uint border = 2; 00159 uint hdw = itsHeadingDirectionMap.getWidth(); 00160 uint hdh = itsHeadingDirectionMap.getHeight(); 00161 00162 // the number of inputs that each MSTd cells received 00163 uint m = PHM_NUM_MSTD_INPUT_NEURONS; // can change it to 20 from 30 00164 00165 // the number of neurons that each direction draw from 00166 uint n = PHM_NUM_MSTD_POPULATION_NEURONS; 00167 00168 // compute likelihood for each direction Tj 00169 for(uint jx = border; jx < hdw - border; jx++) 00170 { 00171 for(uint jy = border; jy < hdh - border; jy++) 00172 { 00173 float U = 0.0; 00174 00175 //Timer tim(1000000); tim.reset(); 00176 00177 // pick m random flows 00178 std::vector<uint> lind = pickRandomLocations(m); 00179 std::vector<Point2D<float> > points = getLocations(lind); 00180 std::vector<Point2D<float> > flows = getFlowVectors(lind); 00181 00182 //uint64 t1 = tim.get(); 00183 00184 // compute C(T) 00185 Image<float> C = computeC(points, jx, jy); 00186 00187 //uint64 t2 = tim.get(); 00188 00189 // compute orthogonal complement: c_perp 00190 // using the above random points 00191 Image<float> c_perp = computeOrthogonalComplement(C); 00192 00193 //LINFO("time: %f", t1/1000.0); 00194 //LINFO("time: %f", (t2 -t1)/1000.0); 00195 //LINFO("time: %f", tim.get()/1000.0); 00196 //Raster::waitForKey(); 00197 // for each MT node in the Tj direction population 00198 for(uint l = 0; l < n; l++) 00199 { 00200 // number of null space dimension 00201 uint nndims = c_perp.getWidth(); 00202 00203 // pick a random vector c_perp_l from c_perp 00204 uint rvindex = uint(nndims * float(rand())/(RAND_MAX + 1.0F)); 00205 Image<double> c_perp_l = 00206 crop(c_perp, Point2D<int>(rvindex, 0), Dims(1,2*m)); 00207 00208 Image<double>::iterator cpli = c_perp_l.beginw(); 00209 00210 // for each input MT neuron to the node 00211 double total = 0.0; 00212 for(uint i = 0; i < m; i++) 00213 { 00214 // calculate the neuron i firing rate 00215 double dx = flows[i].i; 00216 double dy = flows[i].j; 00217 00218 total += dx*(*cpli++); //c_perp_l.getVal(0, i*2); //(*cpli++); 00219 total += dy*(*cpli++); //c_perp_l.getVal(0, i*2+1); //(*cpli++); 00220 00221 // for each direction in the location 00222 //for(uint k = 0; k < PHM_NUM_DIRECTIONS; k++) 00223 // { 00224 //float J_ijkl = itsWeights[jx][jy][k].getVal(pt); 00225 //float s_ik = itsMTfeatures[k].getVal(pt); 00226 00227 //total += * s_ik; 00228 // } 00229 } 00230 00231 // firing rate using sigmoid function 00232 // do the opposite as well to get an optimal result 00233 // near zero 00234 double ul = sigmoid(PHM_MU - total) + sigmoid(PHM_MU + total) -1.0; 00235 //double ul = -fabs(total); 00236 00237 //LINFO("U[%d] Total: %f -> %f", l, total, ul); 00238 U += ul;//-pow(ul,2.0); 00239 } 00240 LINFO("j[%3d %3d]: U: %f",jx,jy, U/n); 00241 00242 //if(jx != 2 & jy != ) U = 0.0; 00243 00244 itsHeadingDirectionMap.setVal(jx,jy, U/n); 00245 } 00246 } 00247 00248 if(itsWin.is_invalid()) 00249 itsWin.reset(new XWinManaged(itsCurrentImage.getDims(), 0, 0, "Pop Heading Map")); 00250 itsWin->setDims(itsCurrentImage.getDims()); 00251 itsWin->drawImage(zoomXY(itsHeadingDirectionMap, PHM_FOE_STEP),0,0); 00252 LINFO("map"); 00253 Raster::waitForKey(); 00254 00255 } 00256 00257 // ###################################################################### 00258 double PopulationHeadingMap::sigmoid(double x) 00259 { 00260 return 1.0F/(1.0F + pow(M_E,-x)); 00261 } 00262 00263 // ###################################################################### 00264 std::vector<uint> PopulationHeadingMap::pickRandomLocations(uint m) 00265 { 00266 std::vector<uint> locIndexes(m); 00267 00268 std::vector<Point2D<float> > flowLocations = 00269 itsOpticalFlow->getFlowLocations(); 00270 uint numFlows = flowLocations.size(); 00271 00272 // make sure there is no duplicate locations 00273 std::vector<bool> picked(numFlows); 00274 for(uint i = 0; i < numFlows; i++) picked[i] = false; 00275 00276 // get n random locations 00277 for(uint i = 0; i < m; i++) 00278 { 00279 uint rindex = 00280 uint(numFlows * float(rand())/(RAND_MAX + 1.0F)); 00281 00282 while(picked[rindex]) 00283 rindex = uint(numFlows * float(rand())/(RAND_MAX + 1.0F)); 00284 00285 locIndexes[i] = rindex; 00286 picked[rindex] = true; 00287 } 00288 00289 return locIndexes; 00290 } 00291 00292 // ###################################################################### 00293 std::vector<Point2D<float> > 00294 PopulationHeadingMap::getLocations(std::vector<uint> lind) 00295 { 00296 uint m = lind.size(); 00297 std::vector<Point2D<float> > locs(m); 00298 00299 std::vector<Point2D<float> > flowLocations = 00300 itsOpticalFlow->getFlowLocations(); 00301 00302 //Image<PixRGB<byte> > img = itsOpticalFlowImage; 00303 00304 // get n random locations 00305 for(uint i = 0; i < m; i++) 00306 { 00307 locs[i] = flowLocations[lind[i]]; 00308 00309 //drawDisk(img, Point2D<int>(locs[i].i, locs[i].j), 2, PixRGB<byte>(255,255,0)); 00310 } 00311 00312 // if(itsWin.is_invalid()) 00313 // itsWin.reset(new XWinManaged(img.getDims(), 0, 0, "Pop Heading Map")); 00314 // itsWin->setDims(img.getDims()); 00315 // itsWin->drawImage(img,0,0); 00316 // LINFO("random points"); 00317 // Raster::waitForKey(); 00318 00319 00320 return locs; 00321 } 00322 00323 // ###################################################################### 00324 std::vector<Point2D<float> > 00325 PopulationHeadingMap::getFlowVectors(std::vector<uint> lind) 00326 { 00327 uint m = lind.size(); 00328 std::vector<Point2D<float> > flows(m); 00329 00330 std::vector<rutz::shared_ptr<FlowVector> > fv = 00331 itsOpticalFlow->getFlowVectors(); 00332 00333 // get n random locations 00334 for(uint i = 0; i < m; i++) 00335 { 00336 flows[i] = 00337 Point2D<float> 00338 (fv[lind[i]]->p2.i - fv[lind[i]]->p1.i, 00339 fv[lind[i]]->p2.j - fv[lind[i]]->p1.j); 00340 } 00341 00342 return flows; 00343 } 00344 00345 // ###################################################################### 00346 Image<double> 00347 PopulationHeadingMap::computeC 00348 (std::vector<Point2D<float> > points, uint jx, uint jy) 00349 { 00350 // NOTE_FIXXX: compute the focal length from the two initial images 00351 float f = itsFocalLength; 00352 //LINFO("Focal Length: %f", f); 00353 00354 int w = itsCurrentImage.getWidth(); 00355 int h = itsCurrentImage.getHeight(); 00356 00357 // create C(T) 00358 uint m = points.size(); 00359 Image<double> C(m+3, 2*m, ZEROS); 00360 00361 double Tx = double(jx)*PHM_FOE_STEP - double(w/2); 00362 double Ty = double(jy)*PHM_FOE_STEP - double(h/2); 00363 double Tz = f; 00364 00365 double wh = w/2; 00366 double hh = h/2; 00367 00368 // fill matrix for every point 00369 for(uint i = 0; i < m; i++) 00370 { 00371 // make the center 00372 double x = points[i].i - wh; 00373 double y = points[i].j - hh; 00374 00375 //LINFO("[%f %f]:{%f, %f, %f}", x,y, Tx,Ty,Tz); 00376 00377 // compute A(x_i,y_i)T 00378 C.setVal(i, i*2, -f*Tx + x*Tz); 00379 C.setVal(i, i*2+1, -f*Ty + y*Tz); 00380 00381 // compute B(x_i,y_i) 00382 C.setVal(m, i*2, x*y/f ); 00383 C.setVal(m+1, i*2, -f-(x*x/f) ); 00384 C.setVal(m+2, i*2, y ); 00385 C.setVal(m, i*2+1, f + (y*y/f)); 00386 C.setVal(m+1, i*2+1, -x*y/f ); 00387 C.setVal(m+2, i*2+1, -x ); 00388 } 00389 00390 // for(int j = 0; j < C.getHeight(); j++) 00391 // { 00392 // for(int i = 0; i < C.getWidth(); i++) 00393 // { 00394 // printf("%9.3f ", C.getVal(i,j)); 00395 // } 00396 // printf("\n"); 00397 // } 00398 // Raster::waitForKey(); 00399 00400 return C; 00401 } 00402 00403 // ###################################################################### 00404 Image<double> 00405 PopulationHeadingMap::computeOrthogonalComplement(Image<double> C) 00406 { 00407 // perform SVD on C; C = [U][S]([V]T) 00408 Image<double> U; 00409 Image<double> S; 00410 Image<double> V; 00411 svd(C, U, S, V, SVD_LAPACK, SVD_FULL); 00412 00413 // LINFO("C[%3d %3d] = U[%3d %3d] S[%3d %3d] V[%3d %3d]", 00414 // C.getHeight(), C.getWidth(), 00415 // U.getHeight(), U.getWidth(), 00416 // S.getHeight(), S.getWidth(), 00417 // V.getHeight(), V.getWidth() ); 00418 00419 uint m = C.getHeight(); 00420 uint n = C.getWidth(); 00421 uint nindex = n; 00422 for(uint i = 0; i < n; i++) 00423 { 00424 double val = S.getVal(i,i); 00425 //LINFO("[%3d]: %f", i, val); 00426 00427 // figure out how many dimension the nullspace is 00428 if(val < 0.00000001) nindex = i; 00429 } 00430 00431 uint nspacedims = m - nindex; 00432 00433 //LINFO("nindex: %d nspacedims: %d", nindex, nspacedims); 00434 00435 Image<double> Cperp = 00436 crop(U, Point2D<int>(nindex,0),Dims(nspacedims,m)); 00437 00438 00439 //Image<double> res = matrixMult(transpose(C),Cperp); 00440 //print(res,std::string("res")); 00441 //Raster::waitForKey(); 00442 00443 00444 return Cperp; 00445 } 00446 00447 // ###################################################################### 00448 void PopulationHeadingMap::print(Image<double> img, const std::string& name) 00449 { 00450 LINFO("%s: %d by %d", name.c_str(), img.getWidth(), img.getHeight()); 00451 for(int j = 0; j < img.getHeight(); j++) 00452 { 00453 for(int i = 0; i < img.getWidth(); i++) 00454 { 00455 printf("%13.5f ", img.getVal(i,j)); 00456 } 00457 printf("\n"); 00458 } 00459 } 00460 // ###################################################################### 00461 /* So things look consistent in everyone's emacs... */ 00462 /* Local Variables: */ 00463 /* indent-tabs-mode: nil */ 00464 /* End: */ 00465 00466 #endif // ROBOTS_BEOBOT2_NAVIGATION_FOENAVIGATION_POPULATIONHEADINGMAP_C_DEFINED