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