ImageSpring.C

Go to the documentation of this file.
00001 /*!@file Beobot/ImageSpring.C derived from the image template class; all the
00002   pixels are linked to theirs neighbors with springs */
00003 
00004 // //////////////////////////////////////////////////////////////////// //
00005 // The iLab Neuromorphic Vision C++ Toolkit - Copyright (C) 2001 by the //
00006 // University of Southern California (USC) and the iLab at USC.         //
00007 // See http://iLab.usc.edu for information about this project.          //
00008 // //////////////////////////////////////////////////////////////////// //
00009 // Major portions of the iLab Neuromorphic Vision Toolkit are protected //
00010 // under the U.S. patent ``Computation of Intrinsic Perceptual Saliency //
00011 // in Visual Environments, and Applications'' by Christof Koch and      //
00012 // Laurent Itti, California Institute of Technology, 2001 (patent       //
00013 // pending; application number 09/912,225 filed July 23, 2001; see      //
00014 // http://pair.uspto.gov/cgi-bin/final/home.pl for current status).     //
00015 // //////////////////////////////////////////////////////////////////// //
00016 // This file is part of the iLab Neuromorphic Vision C++ Toolkit.       //
00017 //                                                                      //
00018 // The iLab Neuromorphic Vision C++ Toolkit is free software; you can   //
00019 // redistribute it and/or modify it under the terms of the GNU General  //
00020 // Public License as published by the Free Software Foundation; either  //
00021 // version 2 of the License, or (at your option) any later version.     //
00022 //                                                                      //
00023 // The iLab Neuromorphic Vision C++ Toolkit is distributed in the hope  //
00024 // that it will be useful, but WITHOUT ANY WARRANTY; without even the   //
00025 // implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR      //
00026 // PURPOSE.  See the GNU General Public License for more details.       //
00027 //                                                                      //
00028 // You should have received a copy of the GNU General Public License    //
00029 // along with the iLab Neuromorphic Vision C++ Toolkit; if not, write   //
00030 // to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,   //
00031 // Boston, MA 02111-1307 USA.                                           //
00032 // //////////////////////////////////////////////////////////////////// //
00033 //
00034 // Primary maintainer for this file: Laurent Itti <itti@usc.edu>
00035 // $HeadURL: svn://isvn.usc.edu/software/invt/trunk/saliency/src/Beobot/ImageSpring.C $
00036 // $Id: ImageSpring.C 9412 2008-03-10 23:10:15Z farhan $
00037 //
00038 
00039 #include "Beobot/ImageSpring.H"
00040 
00041 #include "Channels/Jet.H"
00042 #include "Image/ColorOps.H"
00043 #include "Image/DrawOps.H"
00044 #include "Image/FilterOps.H"
00045 #include "Image/IO.H"
00046 #include "Image/Image.H"
00047 #include "Image/MathOps.H"
00048 #include "Image/Pixels.H"
00049 #include "Image/ShapeOps.H"
00050 #include "Image/Transforms.H"
00051 #include "Util/Assert.H"
00052 #include "Util/MathFunctions.H"
00053 
00054 
00055 // ######################################################################
00056 template<class T>
00057 void ImageSpring<T>::getStats()
00058 {
00059   ASSERT(this->initialized());
00060 
00061   // initialize mean as a zero Jet, same size as the image elements
00062   // and initialize the others on the same model
00063   mean = this->getVal(0); mean *= 0.0f; weight = mean; // initialize Jet for weight
00064   stdev = mean; T mom2(mean);
00065 
00066   for (int n = 0; n < nbHeuristic; n++)
00067     {
00068       T randomPixel = this->getVal(randomUpToNotIncluding(this->getSize()));
00069       mean += randomPixel;
00070       inplaceSquare(randomPixel);
00071       mom2 += randomPixel;
00072     }
00073   mean /= (float)nbHeuristic;
00074   mom2 /= (float)nbHeuristic;
00075   T mean2(mean); inplaceSquare(mean2);
00076 
00077   // E( ( X-E(X) )^2) = E(X^2)-E(X)^2
00078   // we might have negative values because of precision issues
00079   stdev = mom2;
00080   stdev -= mean2;
00081   inplaceRectify(stdev);
00082   stdev = sqrt(stdev);
00083   weight = inverse(stdev, 1.0f);  // load weight values
00084 }
00085 
00086 // ######################################################################
00087 template<class T>
00088 void ImageSpring<T>::getStatsDist()
00089 {
00090   ASSERT(this->initialized());
00091   meanDist = 0.0; double mom2 = 0.0;
00092 
00093   for (int n = 0; n < nbHeuristic; n++)
00094     {
00095       int randomIndex = randomUpToNotIncluding(this->getSize());
00096       T randomPixel = this->getVal(randomIndex);
00097 
00098       bool neighborDefined = false; int neighborNumber, neighborIndex;
00099       int attempt = 0;
00100       while (! neighborDefined && attempt++ < 10)
00101         {
00102           neighborNumber = randomUpToNotIncluding(nbNeighbors) + 1;
00103           neighborDefined =
00104             getNeighbor(randomIndex, neighborNumber, neighborIndex);
00105         }
00106       if (neighborDefined) {
00107         T randomNeighbor = this->getVal(neighborIndex);
00108         double d = distance(randomPixel, randomNeighbor, weight);
00109         meanDist += d; mom2 += squareOf(d);
00110       } else n --;
00111     }
00112   meanDist /= (double)nbHeuristic;
00113   mom2 /= (double)nbHeuristic;
00114 
00115   // E( ( X-E(X) )^2) = E(X^2)-E(X)^2
00116   stdevDist = sqrt(mom2 - squareOf(meanDist));
00117 }
00118 
00119 // ######################################################################
00120 template<class T>
00121 void ImageSpring<T>::initClustering(bool initPosMasses)
00122 {
00123   // at the begining the masses and the pixel are at the same place
00124   if (initPosMasses)
00125     {
00126       for (int x = 0; x < this->getWidth(); x++)
00127         for (int y = 0; y < this->getHeight(); y++)
00128           setPos(Point2D<int>(x, y), float(x), float(y));
00129       memcpy(oldPosX, posX, this->getSize() * sizeof(float));
00130       memcpy(oldPosY, posY, this->getSize() * sizeof(float));
00131     }
00132 
00133   // initialize the image statistics & springs stiffnesses:
00134   getStats(); getStatsDist(); computeStiff();
00135 }
00136 
00137 // ######################################################################
00138 template<class T>
00139 void ImageSpring<T>::computeStiff(void)
00140 {
00141   for (int currentIndex = 0; currentIndex < this->getSize(); currentIndex++)
00142     {
00143       Point2D<int> currentPoint;
00144       getXY(currentIndex, currentPoint);
00145 
00146       // the masses should always try to come back to the pixels
00147       // they come from (to ensure spatial coherence):
00148       setStiff(currentPoint, 0, 0.5f);
00149 
00150       T currval = this->getVal(currentIndex);
00151 
00152       for (int n = 1; n <= nbNeighbors; n++)
00153         {
00154           int neighborIndex;
00155           if (getNeighbor(currentIndex, n, neighborIndex))
00156             {
00157               float alikeness = distance(currval,this->getVal(neighborIndex),weight);
00158 
00159               // we normalize that;
00160               // note the - sign (alikeness high if distance small)
00161               alikeness = -2.0f * (alikeness - meanDist) / stdevDist;
00162 
00163               if (alikeness > 0.0f) setStiff(currentPoint, n, alikeness);
00164               else setStiff(currentPoint, n, 0.0);
00165             }
00166         }
00167     }
00168 }
00169 
00170 // ######################################################################
00171 template<class T>
00172 void ImageSpring<T>::computePos( const float dt )
00173 {
00174   float *newPosX = new float[this->getSize()];
00175   float *newPosY = new float[this->getSize()];
00176 
00177   for (int x = 0; x < this->getWidth(); x++)
00178     for (int y = 0; y < this->getHeight(); y++)
00179       {
00180         int ci; getIndex(Point2D<int>(x, y), ci);
00181 
00182         // the position of the current mass
00183         float X = posX[ci], Y = posY[ci];
00184 
00185         // the force on the current mass; first from the grid:
00186         float fx = stiff[ci][0] * (float(x) - X);
00187         float fy = stiff[ci][0] * (float(y) - Y);
00188 
00189         for (int n = 1; n <= nbNeighbors; n++)
00190           {
00191             int neighborIndex;
00192             if (getNeighbor(ci, n, neighborIndex))
00193               {
00194                 // the stiffness of the current spring
00195                 float stif = stiff[ci][n];
00196 
00197                 // update the force
00198                 fx += stif * (posX[neighborIndex] - X);
00199                 fy += stif * (posY[neighborIndex] - Y);
00200               }
00201           }
00202 
00203         // friction
00204         const float gamma = 1.0f;
00205 
00206         // update position according to equations
00207         newPosX[ci] = 2.0f * posX[ci] - oldPosX[ci] + dt * dt * fx
00208           - dt * gamma * (posX[ci] - oldPosX[ci]);
00209         newPosY[ci] = 2.0f * posY[ci] - oldPosY[ci] + dt * dt * fy
00210           - dt * gamma * (posY[ci] - oldPosY[ci]);
00211       }
00212 
00213   // update the position arrays:
00214   delete [] oldPosX; delete [] oldPosY; oldPosX = posX; oldPosY = posY;
00215   posX = newPosX; posY = newPosY;
00216 }
00217 
00218 // ######################################################################
00219 template<class T>
00220 float ImageSpring<T>::getDistanceMasses(const int idx1, const int idx2) const
00221 { return sqrt(squareOf(posX[idx1]-posX[idx2]) + squareOf(posY[idx1]-posY[idx2])); }
00222 
00223 // ######################################################################
00224 template <class T>
00225 void ImageSpring<T>::getPositions(Image< PixRGB<byte> >& img, const int zoom)
00226 {
00227   img.resize(this->getWidth() * zoom, this->getHeight() * zoom, true); // clear
00228   float maxstiff = 0.0;
00229 
00230   // first draw the springs:
00231   for (int i = 0; i < this->getSize(); i ++)
00232     {
00233       Point2D<int> pp(int(zoom * (posX[i] + 0.5)), int(zoom * (posY[i] + 0.5)));
00234       int ni;
00235 
00236       for (int n = 1; n <= nbNeighbors; n ++)
00237         if (getNeighbor(i, n, ni))
00238           {
00239             Point2D<int> np(int(zoom * (posX[ni]+0.5)), int(zoom * (posY[ni]+0.5)));
00240             drawLine(img, pp, np,
00241                      PixRGB<byte>(150.0f + 50.0f * stiff[i][n],
00242                                   100.0f * stiff[i][n],
00243                                   0));
00244             if (stiff[i][n] > maxstiff) maxstiff = stiff[i][n];
00245           }
00246     }
00247   //LDEBUG("Max stiffness = %f", maxstiff);
00248 
00249   // now draw the masses:
00250   PixRGB<byte> blue(0, 0, 255);
00251   for (int i = 0; i < this->getSize(); i ++)
00252     {
00253       Point2D<int> pp(int(zoom * (posX[i] + 0.5)), int(zoom * (posY[i] + 0.5)));
00254       drawDisk(img, pp, 2, blue);
00255     }
00256 }
00257 
00258 //######################################################################
00259 template <class T>
00260 void ImageSpring<T>::goGraph(Image<int32> &marked, const int currentIndex,
00261                              const int32 color, const int begin)
00262 {
00263   // how many of my neighbors are already in the group 'color' ?
00264   int nbNeighborsSameColor = 0, neighborIndex;
00265   for (int n = 1; n <= nbNeighbors; n ++)
00266     if (getNeighbor(currentIndex, n, neighborIndex) &&
00267         marked.getVal(neighborIndex) == color)
00268       nbNeighborsSameColor ++;
00269 
00270   // if less than 5 and we are not just starting the algorithm
00271   // then forget it, I'm not really in this group
00272   if (begin <= 0 && nbNeighborsSameColor < 5) return;
00273 
00274   // Yes, I have many friends in this group, mark me as belonging there
00275   marked.setVal(currentIndex, color);
00276 
00277   // and check if my unmarked and spatially close neighbors are also
00278   // in the group or not.  We decrement begin because the algo is at
00279   // least partly initialized
00280   for (int n = 1; n <= nbNeighbors; n ++)
00281     if (getNeighbor(currentIndex, n, neighborIndex) &&
00282         marked.getVal(neighborIndex) == 0  &&
00283         getDistanceMasses(currentIndex, neighborIndex) < 1.0)
00284       goGraph(marked, neighborIndex, color, begin - 1);
00285 }
00286 
00287 //######################################################################
00288 template <class T>
00289 void ImageSpring<T>::getClusteredImage(const Image< PixRGB<byte> > &scene,
00290                                        Image< PixRGB<byte> > &clusteredImage,
00291                                        Point2D<int> &supposedTrackCentroid,
00292                                        const Point2D<int>& previousTrackCentroid)
00293 {
00294   int preferedMeanX = 0, preferedMeanY = 0;
00295 
00296   Image<int32> marked(this->getDims(), ZEROS);
00297   Image< PixRGB<byte> > clustered(scene.getDims(), NO_INIT);
00298 
00299   int pX = scene.getWidth() / this->getWidth();
00300   int pY = scene.getHeight() / this->getHeight();
00301 
00302   PixRGB<byte> whitePixel(255, 255, 255);
00303 
00304   float highestScore = std::numeric_limits<float>::max();
00305   // to make sure that it will be initialized by first cluster
00306 
00307   // this color is not a color but merely a label
00308   // this is used in image 'marked', 0 being not marked
00309   int32 color = 0;
00310 
00311   for (int x = 0; x < this->getWidth(); x++ )
00312     for (int y = 0; y < this->getHeight(); y++ )
00313       {
00314         color ++;
00315 
00316         int meanX = 0, meanY = 0;
00317 
00318         if (marked.getVal(x, y) == 0) // pixel is NOT marked yet
00319           {
00320             // marks all connex pixels to color
00321             goGraph(marked, x + this->getWidth() * y, color);
00322 
00323             PixRGB<int32> meanPixel(0, 0, 0);
00324             PixRGB<byte> tmpPixel;
00325             int nbPixels = 0;
00326 
00327             // get mean pixel value on newly marked zone:
00328             for (int xp = 0; xp < this->getWidth(); xp ++  )
00329               for (int yp = 0; yp < this->getHeight(); yp ++)
00330                 if (marked.getVal(xp, yp) == color) // this point just marked
00331                   for (int dx = 0; dx < pX; dx ++)
00332                     for (int dy = 0; dy < pY; dy ++)
00333                       {
00334                         scene.getVal(xp * pX + dx, yp * pY + dy, tmpPixel);
00335                         meanPixel += tmpPixel;
00336                         meanX += xp * pX + dx;
00337                         meanY += yp * pY + dy;
00338                         nbPixels ++;
00339                       }
00340             meanPixel /= nbPixels; meanX /= nbPixels; meanY /= nbPixels;
00341 
00342             // do not consider very small groups:
00343             bool ignoreGroup;
00344             if (nbPixels < scene.getWidth() * scene.getHeight() / 30)
00345               { meanPixel.set(0, 0, 0); ignoreGroup = true; }
00346             else
00347               ignoreGroup=false;
00348 
00349             // set all pixels of that zone in 'clustered'
00350             // to that value
00351             for (int xp = 0; xp < this->getWidth(); xp ++)
00352               for (int yp = 0; yp < this->getHeight(); yp ++)
00353                 if (marked.getVal(xp, yp) == color) // this point just marked
00354                   for (int dx = 0; dx < pX; dx ++)
00355                     for (int dy = 0; dy < pY; dy++)
00356                       clustered.setVal(xp * pX + dx, yp * pY + dy, meanPixel);
00357 
00358             // draw the cross at center of gravity
00359             if (!ignoreGroup)
00360               drawCross(clustered, Point2D<int>(meanX, meanY), whitePixel);
00361 
00362 #define LIKE_CENTER 1.0
00363 #define LIKE_PREVIOUS 1.0
00364 #define LIKE_BIG 1.0
00365 
00366             /*
00367               These defines define the behaviour of the algo which will
00368               determine which of the centroids is the track centroid.
00369 
00370               We minimize a cost function :
00371 
00372                LIKE_CENTER : importance of the centroid being close to
00373                the center bottom of the screen
00374 
00375                LIKE_PREVIOUS : ...
00376                the previous centroid
00377 
00378                LIKE_BIG : ... being the centroid of a bug cluster
00379             */
00380 
00381             float currentScore =
00382               LIKE_CENTER *
00383                 sqrt( double( (float)squareOf(meanX - clustered.getWidth()/2) +
00384                               (float)squareOf(meanY - clustered.getHeight()) ) )
00385               / (float)clustered.getHeight()
00386               + LIKE_PREVIOUS *
00387                   sqrt( double( squareOf(meanX - previousTrackCentroid.i) +
00388                                 squareOf(meanY - previousTrackCentroid.j) ) )
00389               / (float)clustered.getHeight()
00390               - LIKE_BIG * ( (float)nbPixels / ((float)clustered.getHeight()*
00391                                                 (float)clustered.getWidth()));
00392             /* note: it's kind of messy but the idea is that all the
00393                components be more or less between 0.0 and 1.0 */
00394 
00395             if ((currentScore < highestScore) && (!ignoreGroup))
00396               // we have a new champion !
00397               {
00398                 preferedMeanX = meanX; preferedMeanY = meanY;
00399                 highestScore = currentScore;
00400               }
00401 
00402           }
00403       }
00404 
00405   // dashed line in the center of the screen
00406   int xp = clustered.getWidth() / 2;
00407   for(int yp = 0; yp < clustered.getHeight(); yp ++)
00408     if (yp % 3 == 0) clustered.setVal(xp, yp, whitePixel);
00409 
00410   // thicker cross at track center of gravity
00411   drawCross(clustered, Point2D<int>(preferedMeanX, preferedMeanY), whitePixel, 5, 2);
00412 
00413   // output of the function
00414   clusteredImage = clustered;
00415   supposedTrackCentroid.i = preferedMeanX;
00416   supposedTrackCentroid.j = preferedMeanY;
00417 }
00418 
00419 // ######################################################################
00420 // Instantiate for float Jets:
00421 template class ImageSpring< Jet<float> >;
00422 
00423 // ######################################################################
00424 /* So things look consistent in everyone's emacs... */
00425 /* Local Variables: */
00426 /* indent-tabs-mode: nil */
00427 /* End: */
Generated on Sun May 8 08:04:29 2011 for iLab Neuromorphic Vision Toolkit by  doxygen 1.6.3