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