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: */