ScaleSpaceOps.C

Go to the documentation of this file.
00001 /*!@file SpaceVariant/ScaleSpaceOps.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: David J. Berg <dberg@usc.edu>
00034 // $HeadURL: svn://isvn.usc.edu:/software/invt/trunk/saliency/src/SpaceVariant/ScaleSpaceOpss.C $
00035 
00036 #include "SpaceVariant/ScaleSpaceOps.H"
00037 #include "Image/ImageSet.H"
00038 #include "Image/Pixels.H"
00039 #include "Image/LowPass.H" //for lowpass5
00040 #include "Util/Assert.H"
00041 
00042 // ######################################################################
00043 // Local Edge implementation
00044 // ######################################################################
00045 LocalEdge::LocalEdge(const Dims& inp_dims, const float& stdc, const float& smult, const float& ori, const uint& length, const uint density) : onReg(), offReg()
00046 { 
00047   // If using a combination center surround scale space pixels (smult > 1.0):
00048   // by fitting two difference of gaussians with different centers and polarity
00049   // we were able to find the best params (center surround var and separation between 
00050   // DoGs) to sin waves of different frequencies. Then we fit a function to 
00051   // the surface created by the optimal paramters to capture their relationship. 
00052   // So, the function below computes the optimal separation given the variance and surround multiplier. 
00053 
00054   // if using a combination of on and off responses (smult < 1.0):
00055   // By fitting difference of guassians (with different centers) to  sine functions,
00056   // a relationship between the variance and the best offset between centers was found.
00057   const float inter = (density == 1) ? 1.0F : (float)(length-1) / float(density-1);  
00058   float dist = -1.0F * ((float)length - 1.0F) / 2.0F;
00059   
00060   float offset;
00061   if (smult < 1.0) 
00062     offset = 3.4110F * stdc;
00063   else
00064     {
00065       const float x1(-5.8399), x2(-3.1659), x3(-1.5079), x4(-0.7451), x5(3.4038);
00066       offset = x1 * exp(smult * x2) + x3 * exp(smult * x4) + x5 * stdc;
00067     }
00068     
00069   const float offset2 = offset/2.0F;
00070   //we will set it up as a vertical edge and then rotate the points with a 2x2 matrix  
00071   for (uint ii = 0; ii < density; ++ii)
00072     {
00073       //compute position along center of edge
00074       const float x1 = dist; const float y1 = 0.0F;
00075       float xon =  x1; float yon = y1 - offset2;
00076       float xoff = x1; float yoff = y1 + offset2;
00077       
00078       const float xonr = xon * cos(ori) - yon * sin(ori);
00079       const float yonr = xon * sin(ori) + yon * cos(ori);
00080       const float xoffr = xoff * cos(ori) - yoff * sin(ori);
00081       const float yoffr = xoff * sin(ori) + yoff * cos(ori);
00082 
00083       if ( (xonr >= 0) && (xonr < inp_dims.w()) && (yonr >= 0) && (yonr < inp_dims.h())
00084            && (xoffr >= 0) && (xoffr < inp_dims.w()) && (yoffr >= 0) && (yoffr < inp_dims.h()))
00085         {
00086           onReg.push_back(std::pair<float, float>(xonr, yonr));
00087           offReg.push_back(std::pair<float, float>(xoffr, yoffr));
00088         }
00089 
00090       dist += inter;
00091     }
00092 }
00093 
00094 // ######################################################################
00095 LocalEdge::LocalEdge() : onReg(), offReg()
00096 { }
00097 
00098 // ######################################################################
00099 LocalEdge::~LocalEdge()
00100 { }
00101 
00102 // ######################################################################
00103 // scale space functions
00104 // ######################################################################
00105 template <class T_or_RGB>
00106 ImageSet<typename promote_trait<T_or_RGB, float>::TP>
00107 getScaleSpace(const Image<T_or_RGB>& input, const float& max_std)
00108 {
00109   //Calculate levels in our scale space. The variance will be 0 at the first
00110   //level, then 2^(L-1), so the level for a given variance is :
00111   //L=log2(var)+1, with var > 1
00112   const float max_variance = max_std*max_std;
00113   const uint l = (max_variance > 4.0) ? log2(max_variance)+1 : 3;//well need to go up to this level (first level is 0)
00114 
00115   //setup our image to blur, converting to float if necessary
00116   typedef typename promote_trait<T_or_RGB, float>::TP TP;  
00117   Image<TP> inp = input; 
00118   ImageSet<TP> pyr;
00119 
00120   //compute scale space
00121   pyr.push_back(inp);//original 0
00122   inp = lowPass5(inp);
00123   pyr.push_back(inp);//2^0, 1
00124   inp = lowPass5(inp);
00125   pyr.push_back(inp);//2^1, 2
00126   inp = lowPass5(lowPass5(inp));
00127   pyr.push_back(inp);//2^2, 4
00128   
00129   for (uint ii = 4; ii <= l; ii+=2)
00130     {
00131       inp = lowPass5yDecY(lowPass5xDecX(inp));
00132       pyr.push_back(inp);//2^ii
00133       inp = lowPass5(lowPass5(inp));
00134       pyr.push_back(inp);//2^(ii+1)
00135     }
00136   return pyr;
00137 }
00138 
00139 // ######################################################################
00140 template <class T_or_RGB>
00141 void addScale(ImageSet<T_or_RGB>& pyr, const float& max_std)
00142 {
00143   ASSERT(pyr.size() >= 4);//otherwisw we didn't first call getScaleSpace
00144  
00145   //Calculate levels in our scale space. The variance will be 0 at the first
00146   //level, then 2^(L-1), so the level for a given variance is :
00147   //L=log2(var)+1, with var > 1
00148   const float max_variance = max_std*max_std;
00149   const uint l = (max_variance > 4.0) ? log2(max_variance)+1 : 3;//well need to go up to this level (first level is 0)
00150   if (pyr.size() <= l)
00151     {
00152       Image<T_or_RGB> inp = pyr.back();
00153       for (uint ii = pyr.size()-1; ii <= l; ii+=2)
00154         {
00155           inp = lowPass5yDecY(lowPass5xDecX(inp));
00156           pyr.push_back(inp);//2^ii
00157           inp = lowPass5(lowPass5(inp));
00158           pyr.push_back(inp);//2^(ii+1)
00159         }
00160     }
00161 }
00162 
00163 // ######################################################################
00164 template <class T_or_RGB>
00165 T_or_RGB getScaleSpacePixel(const ImageSet<T_or_RGB>& pyr, const float& x, const float& y, const float& std)
00166 {
00167   const int nlev = pyr.size();
00168   const float variance = std*std;
00169   const float lev = (variance >= 1.0F) ? log2(variance)+1 : variance;
00170   const int lev1 = int(floor(lev)); ASSERT(lev1 < nlev);
00171   // let's do a getValInterp on the two adjacent levels, then a linear
00172   // interpolation between these two values:
00173 
00174   // we will interpolate between lev and lev+1. Let's compute the
00175   // scaled down (x, y) coordinates for lev:
00176   float facx = 1.0;
00177   float facy = 1.0;
00178   if (lev1 >= 4) //all at original scale below 4
00179     {
00180       facx = pyr[0].getDims().w() / pyr[lev1].getDims().w();
00181       facy = pyr[0].getDims().h() / pyr[lev1].getDims().h();
00182     }
00183 
00184   float ii1 = x / facx, jj1 = y / facy;
00185   const float ww1 = float(pyr[lev1].getWidth() - 1);
00186   const float hh1 = float(pyr[lev1].getHeight() - 1);
00187 
00188   if (ii1 > ww1) ii1 = ww1;
00189   if (jj1 > hh1) jj1 = hh1;
00190 
00191   const T_or_RGB val1 = pyr[lev1].getValInterp(ii1, jj1);
00192 
00193   // if we are at the top level, then we cannot interpolate in the
00194   // scale dimension, so we just use that single level:
00195   if (lev1 == nlev - 1) return val1;
00196 
00197   // otherwise, repeat for level lev+1:
00198   const uint lev2 = lev1 + 1;
00199   facx = 1.0;
00200   facy = 1.0;
00201   if (lev2 >= 4)//all at original scale below 4
00202     {
00203       facx = pyr[0].getDims().w() / pyr[lev2].getDims().w();
00204       facy = pyr[0].getDims().h() / pyr[lev2].getDims().h();
00205     }
00206 
00207   float ii2 = x / facx, jj2 = y / facy;
00208   const float ww2 = float(pyr[lev2].getWidth() - 1);
00209   const float hh2 = float(pyr[lev2].getHeight() - 1);
00210 
00211   if (ii2 > ww2) ii2 = ww2;
00212   if (jj2 > hh2) jj2 = hh2;
00213 
00214   const T_or_RGB val2 = pyr[lev2].getValInterp(ii2, jj2);
00215 
00216   // now a last linear interpolation between these two values:
00217   const float fz = lev - float(lev1);   // fractional z value
00218   return T_or_RGB(val1 + (val2 - val1) * fz); // no need to clamp
00219 }
00220 
00221 // ######################################################################
00222 template <class T_or_RGB>
00223 typename promote_trait<T_or_RGB, float>::TP 
00224 getDiffScaleSpacePixel(const ImageSet<T_or_RGB>& pyr, const float& x, const float& y, 
00225                        const float& std_center, const float& std_surround, const bool on_center)
00226 {
00227   typedef typename promote_trait<T_or_RGB, float>::TP TP;  
00228   const TP c = (TP)getScaleSpacePixel(pyr, x, y, std_center);
00229   const TP s = (TP)getScaleSpacePixel(pyr, x, y, std_surround);
00230   return (on_center) ? (c - s) : (s - c);
00231 }
00232 
00233 // ######################################################################
00234 template <class T_or_RGB>
00235 typename promote_trait<T_or_RGB, float>::TP
00236 getEdgeScaleSpacePixel(const ImageSet<T_or_RGB>& pyr, const float& x, const float& y, const float& std, const LocalEdge& edgemap)
00237 {
00238   typedef typename promote_trait<T_or_RGB, float>::TP TP;  
00239   TP sum = TP();
00240   for (uint ii = 0; ii < edgemap.onReg.size(); ++ii)
00241     {
00242       const float xon = edgemap.onReg[ii].first + x;
00243       const float yon = edgemap.onReg[ii].second + y;
00244       const float xoff = edgemap.offReg[ii].first + x;
00245       const float yoff = edgemap.offReg[ii].second + y;
00246       
00247       const TP on = (TP)getScaleSpacePixel(pyr, xon, yon, std);
00248       const TP off = (TP)getScaleSpacePixel(pyr, xoff, yoff, std);
00249       sum += on - off;
00250     }
00251   sum /= (TP)edgemap.onReg.size();
00252   return sum;
00253 }
00254 
00255 // ######################################################################
00256 template <class T_or_RGB>
00257 typename promote_trait<T_or_RGB, float>::TP
00258 getEdgeScaleSpacePixel(const ImageSet<T_or_RGB>& pyr, const float& x, const float& y, const float& stdc, const float& stds, const LocalEdge& edgemap)
00259 {
00260   typedef typename promote_trait<T_or_RGB, float>::TP TP;  
00261   TP sum = TP();
00262   for (uint ii = 0; ii < edgemap.onReg.size(); ++ii)
00263     {
00264       const float xon = edgemap.onReg[ii].first + x;
00265       const float yon = edgemap.onReg[ii].second + y;
00266       const float xoff = edgemap.offReg[ii].first + x;
00267       const float yoff = edgemap.offReg[ii].second + y;
00268 
00269       const TP on = getDiffScaleSpacePixel(pyr, xon, yon, stdc, stds, true);
00270       const TP off = getDiffScaleSpacePixel(pyr, xoff, yoff, stdc, stds, false);
00271       sum = on + off;
00272     }
00273   sum /= (TP)edgemap.onReg.size();
00274   return sum;
00275 }
00276 
00277 #include "inst/SpaceVariant/ScaleSpaceOps.I"
00278 // ######################################################################
00279 /* So things look consistent in everyone's emacs... */
00280 /* Local Variables: */
00281 /* mode: c++ */
00282 /* indent-tabs-mode: nil */
00283 /* End: */
Generated on Sun May 8 08:42:18 2011 for iLab Neuromorphic Vision Toolkit by  doxygen 1.6.3