TensorOps.C

Go to the documentation of this file.
00001 /*!@file Image/TensorOps.C Mathematical Tensor operations               */
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: Lior Elazary
00033 // $HeadURL: svn://isvn.usc.edu/software/invt/trunk/saliency/src/Image/TensorOps.C $
00034 // $Id: TensorOps.C 14490 2011-02-11 19:46:05Z lior $
00035 
00036 #ifndef IMAGE_TENSOROPS_C_DEFINED
00037 #define IMAGE_TENSOROPS_C_DEFINED
00038 
00039 #include "Image/TensorOps.H"
00040 
00041 // WARNING: Try not include any other "Image*Ops.H" headers here -- if
00042 // you find that you are needing such a header, then the function
00043 // you're writing probably belongs outside Image_TensorOps.C.
00044 #include "Image/Image.H"
00045 #include "Image/Pixels.H"
00046 #include "Image/Range.H"
00047 #include "Image/MatrixOps.H"
00048 #include "Util/Assert.H"
00049 #include "Util/MathFunctions.H"
00050 #include "Util/safecopy.H"
00051 #include "rutz/trace.h"
00052 #include "GUI/DebugWin.H"
00053 
00054 #include <algorithm>
00055 #include <cmath>
00056 #include <climits>
00057 #include <cfloat>
00058 #include <numeric> // for std::accumulate(), etc.
00059 
00060 #if defined(INVT_USE_MMX) || defined(INVT_USE_SSE) || defined(INVT_USE_SSE2)
00061 #include "Util/mmx-sse.H"
00062 #endif
00063 
00064 //Some functions inspired from a matlab version of tensor voting by Trevor Linton
00065 // (http://www.mathworks.com/matlabcentral/fileexchange/21051-tensor-voting-framework)
00066 
00067 
00068 // ######################################################################
00069 template <class T>
00070 TensorField getTensor(const Image<T>& img, int kernelSize)
00071 {
00072 GVX_TRACE(__PRETTY_FUNCTION__);
00073   typedef typename promote_trait<T, float>::TP TF;
00074 
00075   ASSERT( (kernelSize == 3 ) | (kernelSize == 5));
00076   TensorField tensorField(img.getDims(), NO_INIT);
00077 
00078   bool useScharr = true; 
00079 
00080   typename Image<T>::const_iterator src = img.begin();
00081 
00082   const int w = img.getWidth(), h = img.getHeight();
00083   typename Image<TF>::iterator t1 = tensorField.t1.beginw();
00084   typename Image<TF>::iterator t2 = tensorField.t2.beginw();
00085   typename Image<TF>::iterator t3 = tensorField.t3.beginw();
00086   typename Image<TF>::iterator t4 = tensorField.t4.beginw();
00087   TF zero = TF();
00088 
00089   // first rows are all zeros:
00090   switch(kernelSize) {
00091     case 3:
00092       for (int i = 0; i < w; i ++)
00093       {
00094         *t1++ = zero; *t2++ = zero;
00095         *t3++ = zero; *t4++ = zero;
00096       }
00097       src += w;
00098       break;
00099     case 5: //Zero out the two rows
00100       for (int i = 0; i < w*2; i ++)
00101       {
00102         *t1++ = zero; *t2++ = zero;
00103         *t3++ = zero; *t4++ = zero;
00104       }
00105       src += w*2;
00106       break;
00107   }
00108 
00109   // loop over inner rows:
00110   switch(kernelSize)
00111   {
00112     case 3:
00113       for (int j = 1; j < h-1; j ++)
00114       {
00115         // leftmost pixel is zero:
00116         *t1++ = zero; *t2++ = zero;
00117         *t3++ = zero; *t4++ = zero;
00118         ++src;
00119         // loop over inner columns:
00120         if (useScharr)
00121         {
00122           for (int i = 1; i < w-1; i ++)
00123           {
00124             TF valx = -3*src[-1*w + -1] + 0*src[-1*w + 0] + 3*src[-1*w + 1]
00125                     + -10*src[ 0*w + -1] + 0*src[ 0*w + 0] + 10*src[ 0*w + 1]
00126                     + -3*src[ 1*w + -1] + 0*src[ 1*w + 0] + 3*src[ 1*w + 1];
00127 
00128             TF valy =  3*src[-1*w + -1] +  10*src[-1*w + 0] +  3*src[-1*w + 1]
00129                     +  0*src[ 0*w + -1] +  0*src[ 0*w + 0] +  0*src[ 0*w + 1]
00130                     + -3*src[ 1*w + -1] + -10*src[ 1*w + 0] + -3*src[ 1*w + 1];
00131 
00132             *t1++ = valx*valx; *t2++ = valx*valy;
00133             *t3++ = valx*valy; *t4++ = valy*valy;
00134             ++ src;
00135           }
00136         } else {
00137           //Use the sobel operator
00138           for (int i = 1; i < w-1; i ++)
00139           {
00140             TF valx = -1*src[-1*w + -1] + 0*src[-1*w + 0] + 1*src[-1*w + 1]
00141               + -2*src[ 0*w + -1] + 0*src[ 0*w + 0] + 2*src[ 0*w + 1]
00142               + -1*src[ 1*w + -1] + 0*src[ 1*w + 0] + 1*src[ 1*w + 1];
00143 
00144             TF valy =  1*src[-1*w + -1] +  2*src[-1*w + 0] +  1*src[-1*w + 1]
00145               +  0*src[ 0*w + -1] +  0*src[ 0*w + 0] +  0*src[ 0*w + 1]
00146               + -1*src[ 1*w + -1] + -2*src[ 1*w + 0] + -1*src[ 1*w + 1];
00147 
00148             *t1++ = valx*valx; *t2++ = valx*valy;
00149             *t3++ = valx*valy; *t4++ = valy*valy;
00150             ++ src;
00151           }
00152         }
00153 
00154         // rightmost pixel is zero:
00155         *t1++ = zero; *t2++ = zero;
00156         *t3++ = zero; *t4++ = zero;
00157         ++src;
00158       }
00159       break;
00160     case 5:
00161       for (int j = 2; j < h-2; j ++)
00162       {
00163         // leftmost pixel is zero:
00164         *t1++ = zero; *t2++ = zero;
00165         *t3++ = zero; *t4++ = zero;
00166         ++src;
00167         // loop over inner columns:
00168         for (int i = 2; i < w-2; i ++)
00169         {
00170           TF valx = -1*src[-2*w + -2] +  -2*src[-2*w + -1] + 0*src[-2*w + 0] +  2*src[-2*w + 1] + 1*src[-2*w + 2]
00171             + -4*src[-1*w + -2] +  -8*src[-1*w + -1] + 0*src[-1*w + 0] +  8*src[-1*w + 1] + 4*src[-1*w + 2]
00172             + -6*src[ 0*w + -2] + -12*src[ 0*w + -1] + 0*src[ 0*w + 0] + 12*src[ 0*w + 1] + 6*src[ 0*w + 2]
00173             + -4*src[ 1*w + -2] +  -8*src[ 1*w + -1] + 0*src[ 1*w + 0] +  8*src[ 1*w + 1] + 4*src[ 1*w + 2]
00174             + -1*src[ 2*w + -2] +  -2*src[ 2*w + -1] + 0*src[ 2*w + 0] +  2*src[ 2*w + 1] + 1*src[ 2*w + 2];
00175 
00176           TF valy =  1*src[-2*w + -2] +  4*src[-2*w + -1] +   6*src[-2*w + 0] +  4*src[-2*w + 1] +  1*src[-2*w + 2]
00177             +  2*src[-1*w + -2] +  8*src[-1*w + -1] +  12*src[-1*w + 0] +  8*src[-1*w + 1] +  2*src[-1*w + 2]
00178             +  0*src[ 0*w + -2] +  0*src[ 0*w + -1] +   0*src[ 0*w + 0] +  0*src[ 0*w + 1] +  0*src[ 0*w + 2]
00179             + -2*src[ 1*w + -2] + -8*src[ 1*w + -1] + -12*src[ 1*w + 0] + -8*src[ 1*w + 1] + -2*src[ 1*w + 2]
00180             + -1*src[ 2*w + -2] + -4*src[ 2*w + -1] +  -6*src[ 2*w + 0] + -4*src[ 2*w + 1] + -1*src[ 2*w + 2];
00181           
00182           *t1++ = valx*valx; *t2++ = valx*valy;
00183           *t3++ = valx*valy; *t4++ = valy*valy;
00184           ++ src;
00185         }
00186         // rightmost pixel is zero:
00187         *t1++ = zero; *t2++ = zero;
00188         *t3++ = zero; *t4++ = zero;
00189         ++src;
00190       }
00191       break;
00192   }
00193 
00194 
00195   // last rows are all zeros:
00196   switch(kernelSize)
00197   {
00198     case 3:
00199       for (int i = 0; i < w; i ++)
00200       {
00201         *t1++ = zero; *t2++ = zero;
00202         *t3++ = zero; *t4++ = zero;
00203       }
00204       break;
00205     case 5: //last two rows
00206       for (int i = 0; i < w*2; i ++)
00207       {
00208         *t1++ = zero; *t2++ = zero;
00209         *t3++ = zero; *t4++ = zero;
00210       }
00211       break;
00212   }
00213 
00214 
00215   return tensorField;
00216 
00217 }
00218 
00219 
00220 // ######################################################################
00221 Image<float> getTensorMag(const TensorField& tf)
00222 {
00223 GVX_TRACE(__PRETTY_FUNCTION__);
00224 
00225   Image<float> mag(tf.t1.getDims(), NO_INIT);
00226 
00227   Image<float>::iterator magPtr = mag.beginw();
00228 
00229   Image<float>::const_iterator t1 = tf.t1.begin();
00230   Image<float>::const_iterator t4 = tf.t4.begin();
00231   Image<float>::const_iterator stop = tf.t1.end();
00232 
00233   while (t1 != stop)
00234     {
00235       *magPtr = sqrt(*t1 + *t4);
00236 
00237       magPtr++;
00238       ++t1;
00239       ++t4;
00240     }
00241 
00242   return mag;
00243 }
00244 
00245 
00246 // ######################################################################
00247 TensorField getTensor(const EigenSpace& eigen)
00248 {
00249 GVX_TRACE(__PRETTY_FUNCTION__);
00250 
00251   TensorField tf(eigen.l1.getDims(), NO_INIT);
00252 
00253   //Convert from eigen space to tensor, only 2D for now
00254   for(uint i=0; i<eigen.l1.size(); i++)
00255   {
00256     tf.t1.setVal(i, (eigen.l1.getVal(i)*eigen.e1[0].getVal(i)*eigen.e1[0].getVal(i)) +
00257                         (eigen.l2.getVal(i)*eigen.e2[0].getVal(i)*eigen.e2[0].getVal(i)) );
00258 
00259     tf.t2.setVal(i, (eigen.l1.getVal(i)*eigen.e1[0].getVal(i)*eigen.e1[1].getVal(i)) +
00260                         (eigen.l2.getVal(i)*eigen.e2[0].getVal(i)*eigen.e2[1].getVal(i)) );
00261 
00262     tf.t3.setVal(i, tf.t2.getVal(i)); //Should this be optimized out?
00263 
00264     tf.t4.setVal(i, (eigen.l1.getVal(i)*eigen.e1[1].getVal(i)*eigen.e1[1].getVal(i)) +
00265                         (eigen.l2.getVal(i)*eigen.e2[1].getVal(i)*eigen.e2[1].getVal(i)) );
00266   }
00267 
00268   return tf;
00269 
00270 }
00271 
00272 // ######################################################################
00273 EigenSpace getTensorEigen(const TensorField& tf)
00274 {
00275 GVX_TRACE(__PRETTY_FUNCTION__);
00276 
00277   EigenSpace eigen;
00278 
00279   eigen.e1 = ImageSet<float>(2, tf.t1.getDims(), NO_INIT);
00280   eigen.e2 = ImageSet<float>(2, tf.t1.getDims(), NO_INIT);
00281   eigen.l1 = Image<float>(tf.t1.getDims(), NO_INIT);
00282   eigen.l2 = Image<float>(tf.t1.getDims(), NO_INIT);
00283 
00284   for(uint i=0; i<tf.t1.size(); i++)
00285   {
00286     //trace/2
00287     double trace = (tf.t1.getVal(i) + tf.t4.getVal(i))/2;
00288 
00289     double a = tf.t1.getVal(i) - trace;
00290     double b = tf.t2.getVal(i);
00291 
00292     double ab2 = sqrt((a*a) + (b*b));
00293 
00294     eigen.l1.setVal(i, ab2 + trace);
00295     eigen.l2.setVal(i, -ab2 + trace);
00296 
00297     double theta = atan2 (ab2-a, b);
00298 
00299     eigen.e1[0].setVal(i, cos(theta));
00300     eigen.e1[1].setVal(i, sin(theta));
00301     eigen.e2[0].setVal(i, -sin(theta));
00302     eigen.e2[1].setVal(i, cos(theta));
00303   }
00304 
00305   return eigen;
00306 
00307 }
00308 
00309 
00310 void nonMaxSurp(TensorField& tf, float radius)
00311 {
00312   EigenSpace eigen = getTensorEigen(tf);
00313   Image<float> features = eigen.l1-eigen.l2;
00314 
00315   for(int j=0; j<features.getHeight(); j++)
00316     for(int i=0; i<features.getWidth(); i++)
00317     {
00318       float val = features.getVal(i,j);
00319       if(val > 0)
00320       {
00321         float u = eigen.e1[1].getVal(i,j);
00322         float v = eigen.e1[0].getVal(i,j);
00323         float ang=atan(-u/v); 
00324 
00325         //Look to each direction of the edge and
00326         //check if its a max
00327         int dx = int(radius*cos(ang));
00328         int dy = int(radius*sin(ang));
00329 
00330         if (features.coordsOk(i+dx, j-dy) &&
00331             features.coordsOk(i-dx, j+dy) )
00332         {
00333           //Remove the tensor if its not a local maxima
00334           if (val < features.getVal(i+dx, j-dy) ||
00335               val <= features.getVal(i-dx, j+dy) )
00336             tf.setVal(i,j,0);
00337         }
00338       }
00339     }
00340 }
00341 
00342 
00343 // Include the explicit instantiations
00344 #include "inst/Image/TensorOps.I"
00345 
00346 
00347 // ######################################################################
00348 /* So things look consistent in everyone's emacs... */
00349 /* Local Variables: */
00350 /* indent-tabs-mode: nil */
00351 /* End: */
00352 
00353 #endif // !IMAGE_TENSOROPS_C_DEFINED
Generated on Sun May 8 08:05:16 2011 for iLab Neuromorphic Vision Toolkit by  doxygen 1.6.3