00001 /*!@file SceneUnderstanding/Ganglion.C */ 00002 00003 // //////////////////////////////////////////////////////////////////// // 00004 // The iLab Neuromorphic Vision C++ Toolkit - Copyright (C) 2000-2005 // 00005 // by the 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: Lior Elazary <elazary@usc.edu> 00034 // $HeadURL: svn://isvn.usc.edu/software/invt/trunk/saliency/src/plugins/SceneUnderstanding/Ganglion.C $ 00035 // $Id: Ganglion.C 13551 2010-06-10 21:56:32Z itti $ 00036 // 00037 00038 #ifndef GANGLION_C_DEFINED 00039 #define GANGLION_C_DEFINED 00040 00041 #include "plugins/SceneUnderstanding/Ganglion.H" 00042 00043 #include "Image/DrawOps.H" 00044 #include "Image/MathOps.H" 00045 #include "Image/Layout.H" 00046 #include "Simulation/SimEventQueue.H" 00047 #include "Simulation/SimEvents.H" 00048 #include "Media/MediaSimEvents.H" 00049 #include "Channels/InputFrame.H" 00050 #include "Image/Kernels.H" 00051 #include "Image/FilterOps.H" 00052 #include "Image/Convolutions.H" 00053 #include "GUI/DebugWin.H" 00054 #include <math.h> 00055 #include <fcntl.h> 00056 #include <limits> 00057 #include <string> 00058 00059 const ModelOptionCateg MOC_GANGLION = { 00060 MOC_SORTPRI_3, "Ganglion-Related Options" }; 00061 00062 // Used by: SimulationViewerEyeMvt 00063 const ModelOptionDef OPT_GanglionShowDebug = 00064 { MODOPT_ARG(bool), "GanglionShowDebug", &MOC_GANGLION, OPTEXP_CORE, 00065 "Show debug img", 00066 "ganglion-debug", '\0', "<true|false>", "false" }; 00067 //#include "Neuro/NeuroSimEvents.H" 00068 00069 00070 // ###################################################################### 00071 Ganglion::Ganglion(OptionManager& mgr, const std::string& descrName, 00072 const std::string& tagName) : 00073 SimModule(mgr, descrName, tagName), 00074 SIMCALLBACK_INIT(SimEventInputFrame), 00075 SIMCALLBACK_INIT(SimEventSaveOutput), 00076 itsShowDebug(&OPT_GanglionShowDebug, this) 00077 00078 { 00079 init(Dims(320,240)); 00080 } 00081 00082 // ###################################################################### 00083 Ganglion::~Ganglion() 00084 { 00085 00086 } 00087 00088 // ###################################################################### 00089 void Ganglion::init(Dims numCells) 00090 { 00091 Image<float> img(numCells, ZEROS); 00092 itsGanglionCellsMu.push_back(img); 00093 itsGanglionCellsInput.push_back(img); 00094 itsGanglionCellsOutput.push_back(img); 00095 itsGanglionCellsOutput.push_back(img); 00096 00097 img.clear(1.0); 00098 itsGanglionCellsSig.push_back(img); 00099 itsGanglionCellsPrior.push_back(img); 00100 00101 00102 //itsProbe.i = 100; 00103 //itsProbe.j = 220; 00104 itsProbe.i = -1; 00105 itsProbe.j = -1; 00106 00107 //NOTE: Should the ganglion cells be initalizied randomly from the 00108 //using the learned sigma? 00109 00110 } 00111 00112 // ###################################################################### 00113 void Ganglion::onSimEventInputFrame(SimEventQueue& q, 00114 rutz::shared_ptr<SimEventInputFrame>& e) 00115 { 00116 // here is the inputs image: 00117 const Image<PixRGB<byte> > inimg = e->frame().asRgb(); 00118 itsCurrentImg = inimg; 00119 00120 //set the ganglion input 00121 Image<float> rg, by; 00122 getRGBY(inimg, rg, by, 25.0F); 00123 ////Image<byte> input = by + rg + luminance(inimg); 00124 ////TODO should return 3 seperate channels, for better discrimnation 00125 Image<float> in = rg; // + rg + luminance(inimg); 00126 inplaceNormalize(in, 0.0F, 255.0F); 00127 //Image<byte> input = in; 00128 Image<byte> input = luminance(inimg); 00129 00130 // cvEqualizeHist(img2ipl(input),img2ipl(input)); 00131 itsGanglionCellsInput[LUM] = input; 00132 00133 //itsGanglionCellsInput[RG] = rg; 00134 //itsGanglionCellsInput[BY] = by; 00135 00136 evolve(); 00137 00138 //Output the cells 00139 q.post(rutz::make_shared(new SimEventGanglionOutput(this, itsGanglionCellsOutput))); 00140 00141 // Create a new Retina Image from the inputImage, and post it to the queue 00142 //q.post(rutz::make_shared( 00143 // new SimEventRetinaImage( 00144 // this, 00145 // InputFrame(InputFrame::fromRgb(&inimg, q.now())), 00146 // Rectangle(Point2D<int>(0,0), inimg.getDims()), 00147 // Point2D<int>(0,0) 00148 // ) 00149 // ) 00150 // ); 00151 00152 } 00153 00154 // ###################################################################### 00155 void Ganglion::onSimEventSaveOutput(SimEventQueue& q, rutz::shared_ptr<SimEventSaveOutput>& e) 00156 { 00157 if (itsShowDebug.getVal()) 00158 { 00159 // get the OFS to save to, assuming sinfo is of type 00160 // SimModuleSaveInfo (will throw a fatal exception otherwise): 00161 nub::ref<FrameOstream> ofs = 00162 dynamic_cast<const SimModuleSaveInfo&>(e->sinfo()).ofs; 00163 Layout<PixRGB<byte> > disp = getDebugImage(); 00164 ofs->writeRgbLayout(disp, "Ganglion", FrameInfo("Ganglion", SRC_POS)); 00165 00166 // if (SeC<SimEventVisualCortexOutput> e = q.check<SimEventVisualCortexOutput>(this, SEQ_ANY)) 00167 // ofs->writeGray(e->vco(), "SMap", FrameInfo("SMap", SRC_POS)); 00168 00169 } 00170 } 00171 00172 00173 void Ganglion::setBias(const ImageSet<float>& prior) 00174 { 00175 00176 Point2D<int> maxPos; float maxVal; 00177 findMax(prior[0], maxPos, maxVal); 00178 LINFO("maxVal %f", maxVal); 00179 00180 00181 float filter_period = 100; 00182 float elongation = 2.0; 00183 float angle = 90; 00184 int size = -1; 00185 const double major_stddev = filter_period / 30.0; 00186 const double minor_stddev = major_stddev * elongation; 00187 00188 Image<float> gabor = gaborFilter3(major_stddev, minor_stddev, 00189 filter_period, 90, 180 - angle + 90.0 , size); 00190 00191 gabor -= mean(gabor); 00192 00193 // normalize to unit sum-of-squares: 00194 gabor /= sum(squared(gabor)); 00195 00196 gabor *= 20; 00197 gabor += 1.0; 00198 00199 const int w = itsGanglionCellsMu[0].getWidth(), h = itsGanglionCellsMu[0].getHeight(); 00200 const int Nx = gabor.getWidth(), Ny = gabor.getHeight(); 00201 Image<float> result = itsGanglionCellsMu[0]; //(w, h, ZEROS); 00202 //Image<float> result(w,h,ZEROS); 00203 00204 Image<float>::const_iterator sptr = itsGanglionCellsMu[0].begin(); 00205 Image<float>::iterator dptr = result.beginw(); 00206 00207 const int kkk = Nx * Ny - 1; 00208 const int Nx2 = (Nx - 1) / 2; 00209 const int Ny2 = (Ny - 1) / 2; 00210 00211 const float* filter = gabor.getArrayPtr(); 00212 // very inefficient implementation; one has to be crazy to use non 00213 // separable filters anyway... 00214 for (int j = 0; j < h; ++j) // LOOP rows of src 00215 { 00216 const int j_offset = j-Ny2; 00217 00218 for (int i = 0; i < w; ++i) // LOOP columns of src 00219 { 00220 if (prior[0].getVal(i,j) > 1) 00221 { 00222 ////float sum = 0; 00223 const int i_offset = i-Nx2; 00224 const int kj_bgn = std::max(- j_offset, 0); 00225 const int kj_end = std::min(h - j_offset, Ny); 00226 00227 00228 //float filter_period = 100; 00229 //float elongation = 2.0; 00230 //float angle = 90; //(prior[1].getVal(i,j)+M_PI/2)*180/M_PI; 00231 //int size = -1; 00232 //const double major_stddev = filter_period / 30.0; 00233 //const double minor_stddev = major_stddev * elongation; 00234 00235 //Image<float> gabor2 = gaborFilter3(major_stddev, minor_stddev, 00236 // filter_period, 90, 180 - angle + 90.0 , size); 00237 00238 //gabor2 -= mean(gabor2); 00239 00240 //// normalize to unit sum-of-squares: 00241 //gabor2 /= sum(squared(gabor2)); 00242 00243 //gabor2 *= 20; 00244 //gabor2 += 1.0; 00245 00246 00247 for (int kj = kj_bgn; kj < kj_end; ++kj) // LOOP rows of filter 00248 { 00249 const int kjj = kj + j_offset; // row of src 00250 const int src_offset = w * kjj + i_offset; 00251 const int filt_offset = kkk - Nx*kj; 00252 00253 const int ki_bgn = std::max(-i_offset, 0); 00254 const int ki_end = std::min(w-i_offset, Nx); 00255 00256 for (int ki = ki_bgn; ki < ki_end; ++ki) // LOOP cols of filt 00257 { 00258 dptr[ki + src_offset] += sptr[ki + src_offset] * filter[filt_offset - ki]; 00259 //dptr[ki + src_offset] = sptr[ki + src_offset] * 3.0; 00260 } 00261 } 00262 //SHOWIMG(result); 00263 } 00264 00265 } 00266 } 00267 LINFO("Result"); 00268 SHOWIMG(result); 00269 itsGanglionCellsMu[0] = result; 00270 //Create a filtered output 00271 00272 00273 00274 //float scale = 2; 00275 //Image<float> dog = dogFilter<float>(1.75F + 0.5F*scale, ori, (int)(1 + 1*scale)); 00276 00277 //// normalize to zero mean: 00278 //dog -= mean(dog); 00279 00280 //// normalize to unit sum-of-squares: 00281 //dog /= sum(squared(dog)); 00282 00283 00284 00285 //SHOWIMG(itsGanglionCellsPrior[0]); 00286 ////if (maxVal > 1.0) 00287 //{ 00288 // tmp.clear(1.0F); 00289 // maxPos.i = 67 ; maxPos.j = 95; 00290 00291 // //Rectangle rectPos(Point2D<int>(maxPos.i, maxPos.j), Dims(50,3)); 00292 // //Rectangle rectNeg(Point2D<int>(maxPos.i, maxPos.j-3), Dims(50,3)); 00293 // //drawFilledRectOR(tmp, rect, 255.0F, 1, prior[0].getVal(maxPos)); 00294 // //drawFilledRect(tmp, rectPos, 1.01F); 00295 // //drawFilledRect(tmp, rectNeg, 0.95F); 00296 // //drawCircle(tmp, maxPos, 10, 255.0F); 00297 // //inplaceNormalize(tmp, 0.0F, 255.0F); 00298 00299 00300 // itsGanglionCellsPrior[0] = tmp; 00301 00302 // //SHOWIMG(tmp); 00303 // //SHOWIMG(itsGanglionCellsMu[0]); 00304 // itsGanglionCellsMu[0] *= tmp; 00305 // //SHOWIMG(itsGanglionCellsMu[0]); 00306 00307 //} 00308 // else { 00309 // itsGanglionCellsPrior[0].clear(0.0F); 00310 // } 00311 00312 00313 00314 00315 //SHOWIMG(tmp); 00316 00317 00318 } 00319 00320 // ###################################################################### 00321 void Ganglion::setInput(const Image<PixRGB<byte> > &img) 00322 { 00323 00324 //Retina processing 00325 00326 //histogram equlization 00327 00328 00329 //set the ganglion input 00330 //Image<float> rg, by; 00331 //getRGBY(img, rg, by, 25.0F); 00332 Image<byte> input = luminance(img); 00333 if (itsProbe.isValid()) 00334 { 00335 printf("%i %i %i %i %i\n", input.getVal(itsProbe), 00336 input.getVal(itsProbe.i-1, itsProbe.j-1), 00337 input.getVal(itsProbe.i+1, itsProbe.j-1), 00338 input.getVal(itsProbe.i+1, itsProbe.j+1), 00339 input.getVal(itsProbe.i-1, itsProbe.j+1)); 00340 } 00341 00342 // cvEqualizeHist(img2ipl(input),img2ipl(input)); 00343 itsGanglionCellsInput[LUM] = input; 00344 00345 //itsGanglionCellsInput[RG] = rg; 00346 //itsGanglionCellsInput[BY] = by; 00347 00348 00349 evolve(); 00350 } 00351 00352 // ###################################################################### 00353 void Ganglion::evolve() 00354 { 00355 float R = 3.5; //Sensor noise variance 00356 float Q=0.5; //Prcess noise variance 00357 00358 //getchar(); 00359 for(int i=0; i<1; i++) 00360 { 00361 00362 //Update the gagnlion cells 00363 Image<float>::const_iterator inPtr = itsGanglionCellsInput[i].begin(); 00364 Image<float>::const_iterator inStop = itsGanglionCellsInput[i].end(); 00365 00366 Image<float>::iterator gangMuPtr = itsGanglionCellsMu[i].beginw(); 00367 Image<float>::iterator gangPriorPtr = itsGanglionCellsPrior[i].beginw(); 00368 Image<float>::iterator gangSigPtr = itsGanglionCellsSig[i].beginw(); 00369 Image<float>::iterator outMuPtr = itsGanglionCellsOutput[0].beginw(); 00370 Image<float>::iterator outSigPtr = itsGanglionCellsOutput[1].beginw(); 00371 00372 //Kalman filtering for each ganglion cell 00373 while(inPtr != inStop) 00374 { 00375 //Predict 00376 float mu_hat = *gangMuPtr; // + *gangPriorPtr; 00377 float sig_hat = *gangSigPtr + Q; 00378 00379 //update 00380 float K = (sig_hat)/(sig_hat + R); 00381 *gangMuPtr = mu_hat + K * (*inPtr - mu_hat); 00382 *gangSigPtr = (1-K)*sig_hat; 00383 00384 //Calculate surprise KL(P(M|D),P(M)) 00385 //P(M|D) = N(*gangMuPtr, * gangSigPtr); 00386 //P(M) = N(mu_hat, sig_hat); 00387 00388 float surprise = (((*gangMuPtr-mu_hat)*(*gangMuPtr-mu_hat)) + (*gangSigPtr * *gangSigPtr) + (sig_hat*sig_hat)); 00389 surprise = surprise / (2*sig_hat*sig_hat); 00390 surprise += log(sig_hat / *gangSigPtr); 00391 00392 //if (surprise > 0.1) 00393 // *outPtr = *inPtr; 00394 //else 00395 //*outPtr = surprise; 00396 *outMuPtr = *inPtr; //*gangMuPtr; TODO change back 00397 *outSigPtr = *gangSigPtr; 00398 00399 ++inPtr; 00400 ++gangMuPtr; 00401 ++gangPriorPtr; 00402 ++gangSigPtr; 00403 ++outMuPtr; 00404 ++outSigPtr; 00405 } 00406 } 00407 00408 } 00409 00410 Layout<PixRGB<byte> > Ganglion::getDebugImage() 00411 { 00412 Layout<PixRGB<byte> > outDisp; 00413 //Display the results 00414 Image<float> gangIn = itsGanglionCellsInput[0]; 00415 Image<float> gangPercMu = itsGanglionCellsMu[0]; 00416 00417 //SHOWIMG(itsGanglionCellsSig[0]); 00418 00419 00420 //Display result 00421 inplaceNormalize(gangIn, 0.0F, 255.0F); 00422 00423 00424 if (itsProbe.isValid()) 00425 drawCircle(gangIn, itsProbe, 6, 255.0F); 00426 inplaceNormalize(gangPercMu, 0.0F, 255.0F); 00427 //inplaceNormalize(gangPercSig, 0.0F, 255.0F); 00428 //inplaceNormalize(gangOut, 0.0F, 255.0F); 00429 00430 Layout<PixRGB<byte> > disp; 00431 disp = hcat(itsCurrentImg, toRGB(Image<byte>(gangPercMu))); 00432 //disp = hcat(disp, toRGB(Image<byte>(gangPercSig))); 00433 //disp = hcat(disp, toRGB(Image<byte>(gangOut))); 00434 outDisp = vcat(outDisp, disp); 00435 00436 return outDisp; 00437 00438 } 00439 00440 // ###################################################################### 00441 /* So things look consistent in everyone's emacs... */ 00442 /* Local Variables: */ 00443 /* indent-tabs-mode: nil */ 00444 /* End: */ 00445 00446 #endif 00447