SpaceVariantTransforms.C

00001 /*!@file SpaceVariant/SpaceVariantTransform.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/SpaceVariantTransforms.C $
00035 
00036 #include "SpaceVariant/SpaceVariantTransforms.H"
00037 #include "Image/MatrixOps.H" //for flipVertic,flipHoriz
00038 #include "Image/CutPaste.H" //for concat and crop
00039 #include "Image/LowPass.H" //for lowpass5
00040 #include "Image/ImageSet.H" 
00041 #include "Image/Pixels.H" 
00042 #include "Util/Assert.H"
00043 
00044 #include <cmath>
00045 #include <deque>
00046 
00047 #define PI 3.14159265F
00048 #define PI2 PI/2.0F
00049 #define EPS 0.001F
00050 
00051 // ######################################################################
00052 // implementation for SpaceVariantTransform
00053 // ######################################################################
00054 SpaceVariantTransform::SpaceVariantTransform() :
00055   itsLookup(), itsRevLookup(), itsStd(), itsMaxStd(0.0), isInitialized(false)
00056 {
00057 }
00058 
00059 // ######################################################################
00060 SpaceVariantTransform::~SpaceVariantTransform()
00061 {
00062 }
00063 
00064 // ######################################################################
00065 bool SpaceVariantTransform::initialized() const
00066 {
00067   return isInitialized;
00068 }
00069 
00070 // ######################################################################
00071 void SpaceVariantTransform::to(const int x, const int y, 
00072                                int& rings, int& wedges) const
00073 {
00074   std::pair<float, float> p = itsRevLookup.getVal(x, y);
00075   rings = (int)round(p.first); wedges = (int)round(p.second);
00076 }
00077 
00078 // ######################################################################
00079 void SpaceVariantTransform::to(const int x, const int y, 
00080                                float& rings, float& wedges) const
00081 {
00082   std::pair<float, float> p = itsRevLookup.getVal(x, y);
00083   rings = p.first; wedges = p.second;
00084 }
00085 
00086 // ######################################################################
00087 void SpaceVariantTransform::from(const int rings, const int wedges,
00088                                  int& x, int& y) const
00089 {
00090   std::pair<float, float> p = itsLookup.getVal(rings, wedges);
00091   x = (int)round(p.first); y = (int)round(p.second);
00092 }
00093 
00094 // ######################################################################
00095 void SpaceVariantTransform::from(const int rings, const int wedges,
00096                                  float& x, float& y) const
00097 {
00098   std::pair<float,float> p = itsLookup.getVal(rings, wedges);
00099   x = p.first; y = p.second;
00100 }
00101 
00102 // ######################################################################
00103 const Dims& SpaceVariantTransform::getCTDims() const
00104 {
00105   return itsRevLookup.getDims();
00106 }
00107 
00108 // ######################################################################
00109 const Dims& SpaceVariantTransform::getSVDims() const
00110 {
00111   return itsLookup.getDims();
00112 }
00113 
00114 // ######################################################################
00115 const float& SpaceVariantTransform::getRFSize(const uint u, const uint v) const
00116 {
00117   return itsStd.getVal(u,v);
00118 }
00119 
00120 // ######################################################################
00121 float SpaceVariantTransform::getRFSize(const float& u, const float& v) const
00122 {
00123   return itsStd.getValInterp(u,v);
00124 }
00125 
00126 // ######################################################################
00127 const float& SpaceVariantTransform::getRFSize(const uint pos) const
00128 {
00129   return itsStd.getVal(pos);
00130 }
00131 
00132 // ######################################################################
00133 float SpaceVariantTransform::getMaxRFSize() const
00134 {
00135   return itsMaxStd;
00136 }
00137 
00138 // ######################################################################
00139 SpaceVariantTransform::const_iterator SpaceVariantTransform::begin() const
00140 {
00141   return itsLookup.begin();
00142 }
00143   
00144 // ######################################################################
00145 SpaceVariantTransform::const_iterator SpaceVariantTransform::end() const
00146 {
00147   return itsLookup.end();
00148 }
00149 
00150 // ######################################################################
00151 SpaceVariantTransform::const_iterator SpaceVariantTransform::rbegin() const
00152 {
00153   return itsRevLookup.begin();
00154 }
00155   
00156 // ######################################################################
00157 SpaceVariantTransform::const_iterator SpaceVariantTransform::rend() const
00158 {
00159   return itsRevLookup.end();
00160 }
00161 
00162 // #####################################################################
00163 Image<float>::const_iterator SpaceVariantTransform::rfbegin() const
00164 {
00165   return itsStd.begin();
00166 }
00167 
00168 // ######################################################################
00169 Image<float>::const_iterator SpaceVariantTransform::rfend() const
00170 {
00171   return itsStd.end();
00172 }
00173 
00174 // ######################################################################
00175 float SpaceVariantTransform::computeDistance(const float& theta, 
00176                                              const uint cx, const uint cy,
00177                                              const uint imgw, const uint imgh)
00178 {
00179   //input to this function will range from -PI/2 to 3PI/4 
00180   float d = 0.0F;
00181   
00182   //find the angles (from horizontal) that link cx,cy to the corners
00183   //of our image.
00184   const float angle_ru = -1.0F * atan2(cy, imgw-cx);
00185   const float angle_rl = atan2(imgh-cy, imgw-cx);
00186   const float angle_lu = 3.0F*PI2 - atan2(cx, cy);
00187   const float angle_ll = PI2 + atan2(cx, imgh-cy);
00188   
00189   //standard geometry to compute the length of the line from cx,cy to
00190   //the nearest image boundry given angle theta
00191   if ((theta > angle_lu) && (theta <= 3.0F*PI2))
00192     d = cy/cos(3.0F*PI2 - theta);
00193   else if ((theta >= -PI2) && (theta <= angle_ru))
00194     d = cy/cos(theta + PI2);
00195   else if ((theta > angle_ru) && (theta <= angle_rl))
00196     d = (imgw-cx)/cos(std::abs(theta));  
00197   else if ((theta > angle_rl) && (theta <= angle_ll))
00198     d = (imgh-cy)/cos(std::abs(theta - PI2));
00199   else if ((theta > angle_ll) && (theta <= angle_lu))
00200     d = cx/cos(std::abs(PI - theta));
00201   
00202   ASSERT(d>0.0F);
00203   return d;
00204 }
00205 
00206 // ######################################################################
00207 // implementation for FovealTransform
00208 // ######################################################################
00209 FovealTransform::FovealTransform() : u_FoveaSize(0)
00210 { }
00211 
00212 // ######################################################################
00213 FovealTransform::SCALE_TYPE 
00214 FovealTransform::toScaleType(const std::string& scale_type_string)
00215 {
00216   if (scale_type_string.compare("FULL") == 0)
00217     return FULL;
00218   else if (scale_type_string.compare("CROP") == 0)
00219     return CROP;
00220   else
00221     return NONE;
00222 }
00223 
00224 // ######################################################################
00225 void FovealTransform::setup(const uint imgw, const uint imgh,
00226                             const uint rings, const uint wedges,
00227                             const float& alpha, const float& beta,
00228                             const float& gain, const float& exponent, const float& offset, 
00229                             const float& ppdx, const float& ppdy,
00230                             const SCALE_TYPE& scale_type, const float& s, 
00231                             const float& fovea_cuttoff)
00232 {
00233   //ASSERT(imgw % 2 == 0);ASSERT(imgh % 2 == 0);ASSERT(wedges/2 % 2 == 0);
00234 
00235   //initialize all our lookup tables to -1
00236   itsLookup = Image<std::pair<float,float> >(rings*2, wedges/2, NO_INIT);
00237   itsRevLookup = Image<std::pair<float,float> >(imgw, imgh, NO_INIT);
00238   itsStd = Image<float>(rings*2, wedges/2, NO_INIT);
00239   
00240   iterator iterf(itsLookup.beginw()); 
00241   while (iterf != itsLookup.endw())
00242     *iterf++ = std::pair<float,float>(-1.0F, -1.0F);
00243 
00244   iterf = itsRevLookup.beginw();
00245   while (iterf != itsRevLookup.endw())
00246     *iterf++ = std::pair<float,float>(-1.0F, -1.0F);
00247   
00248   Image<float>::iterator iterv(itsStd.beginw()); 
00249   while (iterv != itsStd.endw())
00250     *iterv++ = -1.0F;
00251   
00252   //some variables for our lookup tables
00253   const uint w2 = wedges / 2; //size of hemifield
00254   const uint w22 = w2/2; //visual quadrant size
00255   const float wstep = PI2 / (w22);//wedge step size (in radians)
00256   const float wstep2 = wstep/2;
00257 
00258   const int sx = imgw - 1;
00259   const int sy = imgh - 1;
00260   const float cx = (float)sx/2.0F; //center x of fovea
00261   const float cy = (float)sy/2.0F; //center y of fovea
00262 
00263   //////////////////////////////////////////
00264   //so the basic equation for our space variant transform is:
00265   //r=sqrt(x^2+y^2);theta=atan2(y/x);
00266   //u=s*ln(r*alpha + beta);v=theta;
00267   //
00268   //inverse:
00269   //r=[exp(u/s) - beta]/alpha
00270   //////////////////////////////////////////
00271 
00272   //find our scale factor s=umax/ln(rmax*alpha+beta)
00273   float scale = 0.0F;
00274   if (scale_type == NONE)
00275     scale = s;
00276   else if (scale_type == CROP)
00277     scale = (rings-1-EPS)/log( ((sx>sy) ? cy : cx) * alpha + beta);
00278   else if (scale_type == FULL)
00279     scale = (rings-1-EPS)/log( sqrt(cx*cx + cy*cy) * alpha + beta);
00280 
00281   /*figure out the size of our fovea. Compute the derivitive of u with
00282     respect to r (du/dr), and values of du/dr > c are considered in
00283     the fovea, where c is a user defined value. Normally c=1, as the
00284     fovea is usually considered the expansive area where du/dr >
00285     1. However, the other parameters can be chosen so that there is no
00286     oversampling, so we allow the user to adjust this value. For 
00287     numerical simplicity, we actually compute dr/du and look for dr/du 
00288     < 1*/
00289   if (fovea_cuttoff > 0.0)//ignore fovea size if the cutoff is 0 or less
00290     {
00291       u_FoveaSize = uint(2.0F * scale * log(fovea_cuttoff * alpha * scale));
00292       if (u_FoveaSize < 0)
00293         LFATAL("for the desired transform parameters and foveal cuttoff, there are no pixels "
00294                "in the fovea! Try increasing the fovea size (--retinasv-fovea-size) or "
00295                "increasing the cutoff to be considered the fovea (--retinasv-fovea-cutoff)");
00296     }
00297       
00298   //loop through the rings
00299   for (uint u = 0; u < rings; ++u)
00300     {
00301       //compute ring distance in pixels
00302       float r = (exp(u / scale) - beta)/alpha;
00303       
00304       //loop through all the angles storing the transform in a lookup
00305       //table, processing the right and left hemifields seperately so
00306       //that the right hemifield (all rings, wedges -pi/2 : pi/2) is
00307       //all on the right of the transformed image.
00308       float theta = -PI2 + wstep2;      
00309       for (uint v = 0; v < w2; ++v)//first -pi/2 : pi/2, right visual vield
00310         {          
00311           float xpos = r * cos(theta);
00312           float ypos = r * sin(theta);
00313 
00314           //compute the receptive field size for this ring as std of a guassian
00315           const float deg = sqrt((xpos / ppdx)*(xpos / ppdx)  + (ypos / ppdy)*(ypos / ppdy));//get in distance in deg
00316           const float std = (gain*pow(deg,exponent)+offset)*(ppdx+ppdy)/2;//relationship between deg and std in pixels.
00317           
00318           if (std > itsMaxStd)
00319             itsMaxStd = std;
00320 
00321           xpos += cx;
00322           ypos += cy;
00323 
00324           if (itsRevLookup.coordsOk(xpos, ypos))
00325             {
00326               itsLookup.setVal(rings - 1 + u, v, std::pair<float, float>(xpos, ypos));
00327               itsStd.setVal(rings - 1 + u, v, std);
00328             }
00329           theta += wstep;
00330         }
00331 
00332       for (int v = w2 - 1; v >= 0; --v) //now for pi/2 : 3pi/4, left hemifield
00333         {
00334           float xpos = r * cos(theta);
00335           float ypos = r * sin(theta);
00336           
00337           //compute the receptive field size for this ring as std of a guassian
00338           const float deg = sqrt((xpos / ppdx)*(xpos / ppdx)  + (ypos / ppdy)*(ypos / ppdy));//get in distance in deg
00339           const float std = (gain*pow(deg,exponent)+offset)*(ppdx+ppdy)/2;//relationship between deg and std in pixels.
00340           
00341           if (std > itsMaxStd)
00342             itsMaxStd = std;
00343           
00344           xpos += cx;
00345           ypos += cy;
00346           
00347           if (itsRevLookup.coordsOk(xpos, ypos))
00348             {
00349               itsLookup.setVal(rings - u, v, std::pair<float, float>(xpos, ypos));
00350               itsStd.setVal(rings - u, v, std);
00351             }
00352           theta += wstep;
00353         }
00354     }
00355 
00356   //create a reverse lookup table
00357   const float wscale = (w2 - 1 - EPS) / (PI - EPS);
00358   const float r2 = (rings*2-1)/2;
00359   for (uint x = 0; x < imgw; ++x)
00360     for (uint y = 0; y < imgh; ++y)
00361       {
00362         const float xx = x - cx;
00363         const float yy = y - cy;
00364         const float r = sqrt(xx*xx + yy*yy);
00365         
00366         float theta = atan2(yy,xx);
00367         if (theta < -PI2) 
00368           theta += 2.0F*PI;        
00369         theta += PI2 + EPS;
00370 
00371         //u position (ring)
00372         float u = scale * log(r * alpha + beta) - 0.5;        
00373         
00374         //v position (wedge)
00375         float v;
00376         if (theta > PI)
00377           {           
00378             v = (theta - PI) * wscale + EPS;
00379             v = w2 - 1 - v;
00380             u = r2 + 1 - u;
00381           }
00382         else
00383           {
00384             v = theta * wscale;
00385             u = r2 + u;
00386           }
00387 
00388         //clmap U and V
00389         if (u > (rings*2-1))
00390           u = rings*2-1-EPS;
00391 
00392         if (u < 0)
00393           u = 0;
00394 
00395         if (v > (w2-1))
00396           v= w2-1 - EPS;
00397 
00398         if (v < 0)
00399           v = 0;
00400 
00401         //save coordinate
00402         itsRevLookup.setVal(x, y, std::pair<float,float>(u,v));
00403       }
00404   //all done
00405   isInitialized = true;
00406 }
00407 
00408 // ######################################################################
00409 const uint FovealTransform::getFoveaSize() const
00410 {
00411   return u_FoveaSize;
00412 }
00413 
00414 // ######################################################################
00415 // Free function implementation for SpaceVariantTransform
00416 // ######################################################################
00417 template <class T_or_RGB>
00418 Image<T_or_RGB> transformTo(const SpaceVariantTransform& sv, const Image<T_or_RGB>& input, 
00419                             const ImageSet<typename promote_trait<T_or_RGB, float>::TP>* const cache, const float& offset)
00420 {
00421   typedef typename promote_trait<T_or_RGB, float>::TP TP;
00422   
00423   //compute scale space, the image may get promoted by getScaleSpace
00424   ImageSet<TP> pyr = (cache) ? *cache : getScaleSpace(input, sv.getMaxRFSize());
00425   
00426   //to hold the transformed image. We need to initialize with zeros
00427   //in case of a transform which doesn't completely fill the space 
00428   Image<T_or_RGB> out(sv.getSVDims(), ZEROS);
00429   typename Image<T_or_RGB>::iterator iter(out.beginw());
00430   SpaceVariantTransform::const_iterator sviter(sv.begin());
00431 
00432   uint pos = 0;
00433   while (iter != out.endw())
00434     {
00435       const float var = sv.getRFSize(pos) + offset;
00436       if (sviter->first >= 0.0F) 
00437         *iter = clamped_convert<T_or_RGB>(getScaleSpacePixel(pyr, sviter->first, sviter->second, var));
00438       ++iter; ++sviter; ++pos;
00439     }
00440   return out;
00441 }
00442 
00443 // ######################################################################
00444 template <class T_or_RGB>
00445 Image<T_or_RGB> transformFrom(const SpaceVariantTransform& sv, const Image<T_or_RGB>& input)
00446 {
00447   //to hold the cartesian output image. We need to initialize with zeros
00448   //in case of a transform which doesn't completely fill the space 
00449   Image<T_or_RGB> out(sv.getCTDims(), ZEROS);
00450   typename Image<T_or_RGB>::iterator oiter = out.beginw();
00451   SpaceVariantTransform::const_iterator sviter(sv.rbegin());  
00452   while (oiter != out.endw())
00453     {
00454       float u = sviter->first, v = sviter->second;
00455       if (u >= 0.0F)
00456         *oiter = clamped_convert<T_or_RGB>(input.getValInterp(u, v));
00457       ++oiter;++sviter;
00458     }
00459   return out;
00460 }
00461 
00462 // ######################################################################
00463 template <class T_or_RGB>
00464 Image<typename promote_trait<T_or_RGB, float>::TP> 
00465 transformToDoG(const SpaceVariantTransform& sv, const Image<T_or_RGB>& input, const float& smult, 
00466                const bool centeron, const ImageSet<typename promote_trait<T_or_RGB, float>::TP>* const cache, const float& offset)
00467 {
00468   ASSERT(smult > 1.0F);//enforce that we have a larger surround than center
00469   
00470   typedef typename promote_trait<T_or_RGB, float>::TP TP;
00471   
00472   //compute scale space, the image may get promoted by getScaleSpace
00473   ImageSet<TP> pyr = (cache) ? *cache : getScaleSpace(input, sv.getMaxRFSize() * smult);
00474   
00475   //to hold the transformed image. We need to initialize with zeros
00476   //in case of a transform which doesn't completely fill the space 
00477   Image<TP> out(sv.getSVDims(), ZEROS);
00478   typename Image<TP>::iterator iter(out.beginw());
00479   SpaceVariantTransform::const_iterator sviter(sv.begin());
00480   
00481   uint pos = 0;
00482   while (iter != out.endw())
00483     {
00484       const float stdc = sv.getRFSize(pos) + offset;
00485       const float stds = stdc * smult;
00486       
00487       if (sviter->first >= 0.0F) 
00488         *iter = getDiffScaleSpacePixel(pyr, sviter->first, sviter->second, stdc, stds, centeron);
00489       
00490       ++iter; ++sviter; ++pos;
00491     }
00492   return out;
00493 }
00494 
00495 // ######################################################################
00496 template <class T_or_RGB>
00497 Image<typename promote_trait<T_or_RGB, float>::TP> 
00498 transformToEdge(const SpaceVariantTransform& sv, const Image<T_or_RGB>& input, const Image<LocalEdge>& edgemap, 
00499                 const ImageSet<typename promote_trait<T_or_RGB, float>::TP>* const cache, const float& offset)
00500 {
00501   //promote our image now if necessary
00502   typedef typename promote_trait<T_or_RGB, float>::TP TP;
00503   
00504   //compute scale space, the image may get promoted by getScaleSpace
00505   ImageSet<TP> pyr = (cache) ? *cache : getScaleSpace(input, sv.getMaxRFSize());
00506   
00507   //to hold the transformed image. We need to initialize with zeros
00508   //in case of a transform which doesn't completely fill the space 
00509   Image<TP> out(sv.getSVDims(), ZEROS);
00510   typename Image<TP>::iterator iter(out.beginw());
00511   SpaceVariantTransform::const_iterator sviter(sv.begin());
00512   Image<LocalEdge>::const_iterator edgeiter(edgemap.begin());  
00513 
00514   uint pos = 0;
00515   while (iter != out.endw())
00516     {
00517       const float std = sv.getRFSize(pos) + offset;
00518       if (sviter->first >= 0.0F) 
00519         *iter = getEdgeScaleSpacePixel(pyr, sviter->first, sviter->second, std, *edgeiter);
00520       
00521       ++iter; ++sviter; ++pos;++edgeiter;
00522     }
00523   return out;
00524 }
00525 
00526 // ######################################################################
00527 template <class T_or_RGB>
00528 Image<typename promote_trait<T_or_RGB, float>::TP> 
00529 transformToEdge(const SpaceVariantTransform& sv, const Image<T_or_RGB>& input, const float& smult, const Image<LocalEdge>& edgemap, const ImageSet<typename promote_trait<T_or_RGB, float>::TP>* const cache, const float& offset)
00530 {
00531   ASSERT((smult >= 1.0F) && (smult <=6.0F));//enforce that we have a larger surround than center
00532   
00533   //promote our image now if necessary
00534   typedef typename promote_trait<T_or_RGB, float>::TP TP;
00535   
00536   //compute scale space, the image may get promoted by getScaleSpace
00537   ImageSet<TP> pyr = (cache) ? *cache : getScaleSpace(input, sv.getMaxRFSize() * smult);
00538   
00539   //to hold the transformed image. We need to initialize with zeros
00540   //in case of a transform which doesn't completely fill the space 
00541   Image<TP> out(sv.getSVDims(), ZEROS);
00542   typename Image<TP>::iterator iter(out.beginw());
00543   SpaceVariantTransform::const_iterator sviter(sv.begin());
00544   Image<LocalEdge>::const_iterator edgeiter(edgemap.begin());  
00545 
00546   uint pos = 0;
00547   while (iter != out.endw())
00548     {
00549       const float stdc = sv.getRFSize(pos) + offset;
00550       const float stds = stdc*smult;
00551       if (sviter->first >= 0.0F) 
00552         *iter = getEdgeScaleSpacePixel(pyr, sviter->first, sviter->second, stdc, stds, *edgeiter);
00553       
00554       ++iter; ++sviter; ++pos;++edgeiter;
00555     }
00556   return out;
00557 }
00558 
00559 // ######################################################################
00560 template <class T_or_RGB>
00561 ImageSet<typename promote_trait<T_or_RGB, float>::TP>
00562 transformToDoGPyramid(const SpaceVariantTransform& sv, const Image<T_or_RGB>& input, const float& smult, const bool centeron, const std::vector<float>& scales, const ImageSet<typename promote_trait<T_or_RGB, float>::TP>* const cache)
00563 { 
00564   //promote our image now if necessary
00565   typedef typename promote_trait<T_or_RGB, float>::TP TP;
00566   
00567   ImageSet<TP> retpyr(scales.size(), sv.getSVDims(), NO_INIT);
00568   for (uint ii = 0; ii < scales.size(); ++ii)
00569     retpyr.push_back(transformToDoG(sv, input, smult, centeron, cache, scales[ii]));
00570   
00571   return retpyr;
00572 }
00573 
00574 // ######################################################################
00575 template <class T_or_RGB>
00576 ImageSet<typename promote_trait<T_or_RGB, float>::TP>
00577 transformToEdgePyramid(const SpaceVariantTransform& sv, const Image<T_or_RGB>& input, const Image<LocalEdge>& edgemap, const float& orientation, const uint& length, const float& density, const std::vector<float>& scales, const ImageSet<typename promote_trait<T_or_RGB, float>::TP>* const cache)
00578 {
00579   //promote our image now if necessary
00580   typedef typename promote_trait<T_or_RGB, float>::TP TP;
00581 
00582   ImageSet<TP> retpyr(scales.size(), sv.getSVDims(), NO_INIT);
00583   for (uint ii = 0; ii < scales.size(); ++ii)
00584     retpyr.push_back(transformToEdge(sv, input, edgemap, cache, scales[ii]));
00585   return retpyr;
00586 }
00587 
00588 // ######################################################################
00589 template <class T_or_RGB>
00590 ImageSet<typename promote_trait<T_or_RGB, float>::TP>
00591 transformToEdgePyramid(const SpaceVariantTransform& sv, const Image<T_or_RGB>& input, const float& smult, const Image<LocalEdge>& edgemap, const float& orientation, const uint& length, const float& density, const std::vector<float>& scales, const ImageSet<typename promote_trait<T_or_RGB, float>::TP>* const cache)
00592 {
00593   //promote our image now if necessary
00594   typedef typename promote_trait<T_or_RGB, float>::TP TP;
00595 
00596   ImageSet<TP> retpyr(scales.size(), sv.getSVDims(), NO_INIT);
00597   for (uint ii = 0; ii < scales.size(); ++ii)
00598     retpyr.push_back(transformToEdge(sv, input, smult, edgemap, cache, scales[ii]));
00599   return retpyr;
00600 }
00601 
00602 // ######################################################################
00603 // This needs to be updated to use std instead of variance, but since
00604 // it isn't used anywhere right now, its OK
00605 // ######################################################################
00606 // ######################################################################
00607 // Free function implementation for FovealTransform
00608 // ######################################################################
00609 template <class T_or_RGB>
00610 Image<T_or_RGB> transformToFoveal(const FovealTransform& sv, const Image<T_or_RGB>& input, const bool interp)
00611 {
00612   //promote our image now if necessary
00613   Image<typename promote_trait<T_or_RGB, float>::TP> inp = input;
00614 
00615   //to hold the transformed image. We need to initialize with zeros
00616   //in case of a transform which doesn't completely fill the space 
00617   Image<T_or_RGB> out(sv.getSVDims(), ZEROS);
00618 
00619   float var = 0;
00620   const uint w2 = sv.getSVDims().w()/2;
00621   const uint h = sv.getSVDims().h();
00622 
00623   //loop through all our rings
00624   for (uint r = 0; r < w2; ++r)
00625     {
00626       //lowpass our input image until it is blurred to the variance of
00627       //this rings receptive field variance
00628       const float rf = sv.getRFSize(w2 + r,0);
00629       while ((rf - var) > 0.25F)
00630         {
00631           inp = lowPass3(inp);
00632           var += 0.5F;
00633         }
00634 
00635       //right visual field
00636       for (uint w = 0; w < h; ++w)
00637         {
00638           const uint u = w2 + r;
00639           if (interp == true){
00640             float x, y;
00641             sv.from(u, w, x, y);
00642             if (x >= 0.0F)
00643               out.setVal(u, w, clamped_convert<T_or_RGB>(inp.getValInterp(x,y)));
00644           }
00645           else{
00646             int x, y;
00647             sv.from(u, w, x, y);
00648             if (x > -1)
00649               out.setVal(u, w, clamped_convert<T_or_RGB>(inp.getVal(x, y)));
00650           }
00651         }
00652 
00653       //left visual field
00654       for (uint w = 0; w < h; ++w)
00655         {
00656           const uint u = w2 - r - 1;
00657           if (interp == true){
00658             float x, y;
00659             sv.from(u, w, x, y);
00660             if (x >= 0.0F)
00661               out.setVal(u,w,clamped_convert<T_or_RGB>(inp.getValInterp(x,y)));
00662           }
00663           else{
00664             int x, y;
00665             sv.from(u, w, x, y);
00666             if (x > -1)
00667               out.setVal(u, w, clamped_convert<T_or_RGB>(inp.getVal(x, y)));
00668           }
00669         }
00670     }  
00671   return out;
00672 }
00673 
00674 // ######################################################################
00675 template <class T_or_RGB>
00676 Image<typename promote_trait<T_or_RGB, float>::TP> 
00677 transformToDoGFoveal(const FovealTransform& sv,const Image<T_or_RGB>& input,
00678                      const float& smult, const bool centeron, const bool interp)
00679 {
00680   ASSERT(smult > 1.0F);//enforce that we have a larger surround than center
00681   
00682   //promote our image now if necessary
00683   Image<typename promote_trait<T_or_RGB, float>::TP> inp = input;
00684 
00685   //to hold the transformed image. We need to initialize with zeros
00686   //in case of a transform which doesn't completely fill the space x
00687   Image<T_or_RGB> out(sv.getSVDims(), ZEROS);
00688 
00689   //to hold intermediate filter results
00690   std::deque<Image<typename promote_trait<T_or_RGB, float>::TP> > pyr;
00691 
00692   float var = 0;//image variance
00693   const uint rings = sv.getSVDims().w();
00694   const uint w2 = rings/2;//half widht
00695   const uint h = sv.getSVDims().h();//height
00696   uint rc = 0;//index of current center
00697   float rfc = sv.getRFSize(rc,0);//variance of current center
00698 
00699   //loop over our rings
00700   for (uint r = 0; r < w2; ++r)
00701     {
00702       //enfoce that the surround is always 0.5F greater than the
00703       //center, so that the subtraction just doesn't just cancel out.
00704       float rf = sv.getRFSize(r,0);
00705       if (rf*(smult - 1) >= 0.5F) rf *= smult; else rf += 0.5F;
00706 
00707       //The ideas here is to low pass to the surround size, but pickup
00708       //and store any center rf sizes on the way
00709       while (( rf - var) > 0.25F)
00710         {
00711           //see if we have any centers 
00712           while (((rfc - var) <= 0.25F) && (rc < rings))
00713             {
00714               pyr.push_back(inp);
00715               rfc = sv.getRFSize(++rc, 0);
00716             }
00717           inp = lowPass3(inp);
00718           var += 0.5F;
00719         }
00720 
00721       Image<typename promote_trait<T_or_RGB, float>::TP> diff;//store C-S 
00722 
00723       //calculate our center surround response if any new ones left
00724       if (pyr.size() > 0) {
00725         if (centeron)
00726           {
00727             diff = pyr.front();
00728             pyr.pop_front();
00729             diff -= inp;
00730           }
00731         else
00732           {
00733             diff = inp;
00734             diff -= pyr.front();
00735             pyr.pop_front();
00736           }
00737       }
00738 
00739       //right visual field
00740       for (uint w = 0; w < h; ++w)
00741         {
00742           const uint u = w2 + r;
00743           if (interp == true){
00744             float x, y;
00745             sv.from(u, w, x, y);
00746             if (x >= 0.0F)
00747               out.setVal(u, w, diff.getValInterp(x, y));
00748           }
00749           else{
00750             int x, y;
00751             sv.from(u, w, x, y);
00752             if (x != -1)
00753               out.setVal(u, w, diff.getVal(x, y));
00754           }
00755         }
00756       
00757       //left visual field
00758       for (uint w = 0; w < h; ++w)
00759         {
00760           const uint u = w2 - r - 1;
00761           if (interp == true){
00762             float x, y;
00763             sv.from(u, w, x, y);
00764             if (x >= 0.0F)
00765               out.setVal(u, w, diff.getValInterp(x, y));
00766           }
00767           else {
00768             int x, y;
00769             sv.from(u, w, x, y);
00770             if (x != -1)
00771               out.setVal(u, w, diff.getVal(x, y));
00772           }
00773         }
00774     }
00775   return out;
00776 }
00777 
00778 // ######################################################################
00779 // helper function
00780 // ######################################################################
00781 template <class T_or_RGB>
00782 Image<T_or_RGB> replicateHemifield(const Image<T_or_RGB>& inp, const uint pix)
00783 { 
00784   //take a small chunk from each hemiefield,flip and rotate, then
00785   //concatinate them horizontally, swapping sides.
00786   Image<T_or_RGB> t =
00787     concatX( flipHoriz(flipVertic( crop(inp, Point2D<int>(inp.getWidth()/2, 0),
00788                                         Dims(inp.getWidth()/2, pix)) )),
00789              flipHoriz(flipVertic( crop(inp, Point2D<int>(0, 0),
00790                                         Dims(inp.getWidth()/2, pix)) )) );
00791   
00792   Image<T_or_RGB> b =
00793     concatX( flipHoriz(flipVertic( crop(inp, Point2D<int>(inp.getWidth()/2,
00794                                                           inp.getHeight()-pix),
00795                                         Dims(inp.getWidth()/2, pix)) )),
00796              flipHoriz(flipVertic( crop(inp,Point2D<int>(0,inp.getHeight()-pix),
00797                                         Dims(inp.getWidth()/2, pix)) )) );
00798   
00799   Image<T_or_RGB> output = concatY( concatY(t,inp), b);
00800   return output;
00801 }
00802 
00803 // ######################################################################
00804 template <class T_or_RGB>
00805 void getFoveaPeriphery(const FovealTransform& sv, const Image<T_or_RGB>& ret, 
00806                        Image<T_or_RGB>& fovea, Image<T_or_RGB>& periph)
00807 {
00808   ASSERT(sv.getSVDims() == ret.getDims());
00809     
00810   const uint fix = sv.getFoveaSize();
00811   const uint fix2 = fix / 2;
00812   const uint w = ret.getWidth();
00813   const uint w2 = w / 2;
00814   const uint h = ret.getHeight();
00815 
00816   Point2D<int> a(0,0), b(w2-fix2, 0), c(w2+fix2,0);
00817   Dims ad(b.i, h), bd(fix, h), cd(w - c.i, h);
00818 
00819   Image<T_or_RGB> lh = crop(ret, a, ad);
00820   Image<T_or_RGB> rh = crop(ret, c, cd);
00821   fovea = crop(ret, b, bd);
00822   periph = concatX(lh, rh);
00823 }
00824 
00825 // ######################################################################
00826 Image<LocalEdge> LocalEdgeMap(const SpaceVariantTransform& sv, const float& smult, 
00827                               const float& orientation, const uint& length, const uint density)
00828 {
00829   Image<LocalEdge> out(sv.getSVDims(), NO_INIT);
00830   Image<LocalEdge>::iterator ii(out.beginw());
00831   Image<float>::const_iterator tr(sv.rfbegin());
00832   while (ii != out.end())
00833     {
00834       if (*tr >= 0.0)
00835         *ii = LocalEdge(sv.getCTDims(), *tr, smult, orientation, length, density);
00836       ++ii; ++tr;
00837     }
00838   return out;
00839 }
00840 
00841 #include "inst/SpaceVariant/SpaceVariantTransforms.I"
00842 // ######################################################################
00843 /* So things look consistent in everyone's emacs... */
00844 /* Local Variables: */
00845 /* mode: c++ */
00846 /* indent-tabs-mode: nil */
00847 /* End: */
Generated on Sun May 8 08:42:18 2011 for iLab Neuromorphic Vision Toolkit by  doxygen 1.6.3