PyramidOps.C

Go to the documentation of this file.
00001 /*!@file Image/PyramidOps.C Free functions operating on pyramid data structures */
00002 
00003 // //////////////////////////////////////////////////////////////////// //
00004 // The iLab Neuromorphic Vision C++ Toolkit - Copyright (C) 2001 by the //
00005 // 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: Laurent Itti <itti@usc.edu>
00034 // $HeadURL: svn://isvn.usc.edu/software/invt/trunk/saliency/src/Image/PyramidOps.C $
00035 // $Id: PyramidOps.C 14474 2011-02-04 01:21:21Z dberg $
00036 //
00037 
00038 #include "Image/PyramidOps.H"
00039 
00040 #include "Image/Image.H"
00041 #include "Image/ImageSet.H"
00042 #include "Image/ImageSetOps.H"
00043 #include "Image/FilterOps.H"
00044 #include "Image/LowPassLpt.H"
00045 #include "Image/Kernels.H"
00046 #include "Image/MathOps.H"
00047 #include "Image/ShapeOps.H"
00048 #include "Image/Transforms.H" // for chamfer34()
00049 #include "Image/Pixels.H"
00050 #include "Util/Assert.H"
00051 #include "rutz/trace.h"
00052 
00053 // ############################################################
00054 // ##### Dyadic pyramid pixel operations:
00055 // ############################################################
00056 
00057 // ######################################################################
00058 template <class T>
00059 T getPyrPixel(const ImageSet<T>& pyr,
00060               const Point2D<int>& p, const uint lev)
00061 {
00062   ASSERT(lev < pyr.size());
00063   ASSERT(pyr[0].coordsOk(p));
00064   ASSERT(isDyadic(pyr));
00065 
00066   float fac = float(1 << lev);
00067   float ii = float(p.i) / fac, jj = float(p.j) / fac;
00068   int ww = pyr[lev].getWidth() - 1, hh = pyr[lev].getHeight() - 1;
00069   if (ii > ww) ii = ww; if (jj > hh) jj = hh;
00070 
00071   return pyr[lev].getValInterp(ii, jj);
00072 }
00073 
00074 // ######################################################################
00075 template <class T>
00076 T getPyrPixelNI(const ImageSet<T>& pyr,
00077                 const Point2D<int>& p, const uint lev)
00078 {
00079   ASSERT(lev < pyr.size());
00080   ASSERT(pyr[0].coordsOk(p));
00081   ASSERT(isDyadic(pyr));
00082 
00083   int ii = p.i >> lev, jj = p.j >> lev;
00084   int ww = pyr[lev].getWidth() - 1, hh = pyr[lev].getHeight() - 1;
00085   if (ii > ww) ii = ww; if (jj > hh) jj = hh;
00086 
00087   return pyr[lev].getVal(ii, jj);
00088 }
00089 
00090 // ######################################################################
00091 template <class T>
00092 T getPyrPixel(const ImageSet<T>& pyr,
00093               const float x, const float y, const float z)
00094 {
00095   ASSERT(isDyadic(pyr));
00096 
00097   const uint nlev = pyr.size();
00098 
00099   // let's do a getValInterp on the two adjacent levels, then a linear
00100   // interpolation between these two values:
00101   const uint lev1 = int(floor(z)); ASSERT(lev1 < nlev);
00102 
00103   // we will interpolate between lev and lev+1. Let's compute the
00104   // scaled down (x, y) coordinates for lev:
00105   const float fac1 = float(1 << lev1);
00106   float ii1 = x / fac1, jj1 = y / fac1;
00107   const float ww1 = float(pyr[lev1].getWidth() - 1);
00108   const float hh1 = float(pyr[lev1].getHeight() - 1);
00109   if (ii1 > ww1) ii1 = ww1;
00110   if (jj1 > hh1) jj1 = hh1;
00111 
00112   const T val1 = pyr[lev1].getValInterp(ii1, jj1);
00113 
00114   // if we are at the top level, then we cannot interpolate in the
00115   // scale dimension, so we just use that single level:
00116   if (lev1 == nlev - 1) return val1;
00117 
00118   // otherwise, repeat for level lev+1:
00119   const uint lev2 = lev1 + 1;
00120   const float fac2 = float(2 << lev2);
00121   float ii2 = x / fac2, jj2 = y / fac2;
00122   float ww2 = float(pyr[lev2].getWidth() - 1);
00123   float hh2 = float(pyr[lev2].getHeight() - 1);
00124   if (ii2 > ww2) ii2 = ww2;
00125   if (jj2 > hh2) jj2 = hh2;
00126 
00127   const T val2 = pyr[lev2].getValInterp(ii2, jj2);
00128 
00129   // now a last linear interpolation between these two values:
00130   const float fz = z - float(lev1);   // fractional z value
00131 
00132   return T(val1 + (val2 - val1) * fz); // no need to clamp
00133 }
00134 
00135 // ######################################################################
00136 // ##### Pyramid builder functions:
00137 // ######################################################################
00138 
00139 // ######################################################################
00140 template <class T>
00141 ImageSet<T> buildPyrGaussian(const Image<T>& image,
00142                              int firstlevel, int depth, int filterSize)
00143 {
00144 GVX_TRACE(__PRETTY_FUNCTION__);
00145   ASSERT(image.initialized());
00146 
00147   ImageSet<T> result(depth);
00148 
00149   if (0 >= firstlevel)
00150     result[0] = image;
00151 
00152   Image<T> a = image;
00153   for (int lev = 1; lev < depth; ++lev)
00154     {
00155       if (filterSize == 5)
00156         {
00157           a = lowPass5yDecY(lowPass5xDecX(a,2),2);
00158         }
00159       else
00160         {
00161           a = decX(lowPassX(filterSize, a));
00162           a = decY(lowPassY(filterSize, a));
00163         }
00164 
00165       // NOTE: when lev < firstlevel, we leave result[lev] empty even
00166       // though it isn't any extra work to fill it in; that way, other
00167       // functions that see the result ImageSet might be able to gain
00168       // additional speed-ups by seeing that some of the images are
00169       // empty and avoid processing them further
00170       if (lev >= firstlevel)
00171         result[lev] = a;
00172     }
00173 
00174   return result;
00175 }
00176 
00177 // ######################################################################
00178 template <class T>
00179 ImageSet<T> buildRadialPyrGaussian(const Image<T>& image, int firstlevel, int depth)
00180 {
00181   GVX_TRACE(__PRETTY_FUNCTION__);
00182   ASSERT(image.initialized());
00183 
00184   ImageSet<T> result(depth);
00185   
00186   if (0 >= firstlevel)
00187     result[0] = image;
00188   
00189   Image<T> a = image;
00190   for (int lev = 1; lev < depth; ++lev)
00191     {
00192       a = decXY(lowPassLpt5(a, CROSS_HEMI));
00193       
00194       // NOTE: when lev < firstlevel, we leave result[lev] empty even
00195       // though it isn't any extra work to fill it in; that way, other
00196       // functions that see the result ImageSet might be able to gain
00197       // additional speed-ups by seeing that some of the images are
00198       // empty and avoid processing them further
00199       if (lev >= firstlevel)
00200         result[lev] = a;
00201     }
00202   
00203   return result;
00204 }
00205 
00206 // ######################################################################
00207 template <class T>
00208 ImageSet<T> buildPyrConvolve(const Image<T>& image,
00209                              int firstlevel, int depth,
00210                              const Image<float>& filter,
00211                              ConvolutionBoundaryStrategy boundary)
00212 {
00213 GVX_TRACE(__PRETTY_FUNCTION__);
00214   ASSERT(image.initialized());
00215 
00216   ImageSet<T> result(depth);
00217 
00218   Image<T> base;
00219   for (int lev = 0; lev < depth; ++lev)
00220     {
00221       if (lev == 0) base = image; else base = decXY(lowPass5(base));
00222 
00223       if (lev >= firstlevel)
00224         result[lev] = convolve(base, filter, boundary);
00225     }
00226 
00227   return result;
00228 }
00229 
00230 // ######################################################################
00231 template <class T>
00232 ImageSet<T> buildPyrLaplacian(const Image<T>& image,
00233                               int firstlevel, int depth, int filterSize)
00234 {
00235 GVX_TRACE(__PRETTY_FUNCTION__);
00236   ASSERT(image.initialized());
00237 
00238   ImageSet<T> result(depth);
00239 
00240   Image<T> lpf = lowPass(filterSize, image);
00241 
00242   if (0 >= firstlevel)
00243     result[0] = image - lpf;
00244 
00245   for (int lev = 1; lev < depth; ++lev)
00246     {
00247       const Image<T> dec = decXY(lpf);
00248       lpf = lowPass(filterSize, dec);
00249 
00250       if (lev >= firstlevel)
00251         result[lev] = dec - lpf;
00252     }
00253 
00254   return result;
00255 }
00256 
00257 // ######################################################################
00258 template <class T>
00259 ImageSet<T> buildPyrOrientedFromLaplacian(const ImageSet<T>& laplacian,
00260                                           int filterSize,
00261                                           float theta, float intens,
00262                                           const bool usetab)
00263 {
00264   int attenuation_width = -1;
00265   float spatial_freq = -1.0;
00266 
00267   switch (filterSize)
00268     {
00269     case 5: attenuation_width = 3; spatial_freq = M_PI_2; break;
00270     case 9: attenuation_width = 5; spatial_freq = 2.6; break;
00271     default:
00272       LFATAL("Filter size %d is not supported", filterSize);
00273     }
00274 
00275   // compute oriented filter from laplacian:
00276   ImageSet<T> result(laplacian.size());
00277 
00278   for (size_t lev = 0; lev < result.size(); ++lev)
00279     {
00280       if (laplacian[lev].initialized())
00281         {
00282           result[lev] = orientedFilter(laplacian[lev], spatial_freq,
00283                                        theta, intens, usetab);
00284           // attenuate borders that are overestimated due to filter trunctation:
00285           inplaceAttenuateBorders(result[lev], attenuation_width);
00286         }
00287     }
00288 
00289   return result;
00290 }
00291 
00292 // ######################################################################
00293 template <class T>
00294 ImageSet<T> buildPyrOriented(const Image<T>& image,
00295                              int firstlevel, int depth, int filterSize,
00296                              float theta, float intens,
00297                              const bool usetab)
00298 {
00299 GVX_TRACE(__PRETTY_FUNCTION__);
00300   ASSERT(image.initialized());
00301 
00302   // let's promote the intermediary results to float
00303   typedef typename promote_trait<T, float>::TP TF;
00304 
00305   const ImageSet<TF> laplacian =
00306     buildPyrLaplacian(Image<TF>(image), firstlevel, depth, filterSize);
00307 
00308   const ImageSet<TF> orifilt =
00309     buildPyrOrientedFromLaplacian(laplacian, filterSize, theta,
00310                                   intens, usetab);
00311 
00312   ImageSet<T> result(orifilt.size());
00313   for (size_t i = 0; i < result.size(); ++i)
00314     result[i] = Image<T>(orifilt[i]);
00315 
00316   return result;
00317 }
00318 
00319 // ######################################################################
00320 template <class T>
00321 ImageSet<T> buildPyrLocalAvg(const Image<T>& image, int depth)
00322 {
00323 GVX_TRACE(__PRETTY_FUNCTION__);
00324   ASSERT(image.initialized());
00325 
00326   ASSERT(depth >= 1);
00327 
00328   ImageSet<T> result(depth);
00329 
00330   // only deepest level of pyr is filled with local avg:
00331   const int scale = int(pow(2.0, depth - 1));
00332   result[depth - 1] = quickLocalAvg(image, scale);
00333 
00334   return result;
00335 }
00336 
00337 // ######################################################################
00338 template <class T>
00339 ImageSet<T> buildPyrLocalAvg2x2(const Image<T>& image, int depth)
00340 {
00341 GVX_TRACE(__PRETTY_FUNCTION__);
00342   ASSERT(depth >= 0);
00343 
00344   if (depth == 0)
00345     return ImageSet<T>();
00346 
00347   ImageSet<T> result(depth);
00348   result[0] = image;
00349 
00350   for (int i = 1; i < depth; ++i)
00351     result[i] = quickLocalAvg2x2(result[i-1]);
00352 
00353   return result;
00354 }
00355 
00356 // ######################################################################
00357 template <class T>
00358 ImageSet<T> buildPyrLocalMax(const Image<T>& image, int depth)
00359 {
00360 GVX_TRACE(__PRETTY_FUNCTION__);
00361   ASSERT(image.initialized());
00362 
00363   ASSERT(depth >= 1);
00364 
00365   ImageSet<T> result(depth);
00366 
00367   // only deepest level of pyr is filled with local max:
00368   const int scale = int(pow(2.0, depth - 1));
00369   result[depth - 1] = quickLocalMax(image, scale);
00370 
00371   return result;
00372 }
00373 
00374 // ######################################################################
00375 template <class T>
00376 ImageSet<T> buildPyrGeneric(const Image<T>& image,
00377                             int firstlevel, int depth,
00378                             const PyramidType typ, const float gabor_theta,
00379                             const float intens)
00380 {
00381 GVX_TRACE(__PRETTY_FUNCTION__);
00382   switch(typ)
00383     {
00384     case Gaussian3: return buildPyrGaussian(image, firstlevel, depth, 3);
00385     case Gaussian5: return buildPyrGaussian(image, firstlevel, depth, 5);
00386     case Gaussian9: return buildPyrGaussian(image, firstlevel, depth, 9);
00387 
00388     case Laplacian5: return buildPyrLaplacian(image, firstlevel, depth, 5);
00389     case Laplacian9: return buildPyrLaplacian(image, firstlevel, depth, 9);
00390 
00391     case Oriented5: return buildPyrOriented(image, firstlevel, depth, 5, gabor_theta, intens);
00392     case Oriented9: return buildPyrOriented(image, firstlevel, depth, 9, gabor_theta, intens);
00393 
00394     case QuickLocalAvg: return buildPyrLocalAvg(image, depth);
00395     case QuickLocalMax: return buildPyrLocalMax(image, depth);
00396 
00397     default:
00398       LFATAL("Attempt to create Pyramid of unknown type %d", typ);
00399     }
00400 
00401   ASSERT(false);
00402   return ImageSet<T>(); // "can't happen", but placate compiler
00403 }
00404 
00405 // ######################################################################
00406 ImageSet<float> buildPyrGabor(const ImageSet<float>& gaussianPyr,
00407                               float angle, float filter_period,
00408                               float elongation, int size, int flags)
00409 {
00410 GVX_TRACE(__PRETTY_FUNCTION__);
00411   const double major_stddev = filter_period / 3.0;
00412   const double minor_stddev = major_stddev * elongation;
00413 
00414   // We have to add 90 to the angle here when constructing the gabor
00415   // filter. That's because the angle used to build the gabor filter
00416   // actually specifies the direction along which the grating
00417   // varies. This direction is orthogonal to the the direction of the
00418   // contours that the grating will detect.
00419   const double theta = angle + 90.0f;
00420 
00421   // In concept, we want to filter with four phases of the filter: odd
00422   // on+off, and even on+off (that would be xox, oxo, xo, and ox). But
00423   // since the on version just produces the negation of the off version,
00424   // we can get the summed output of both by just taking the absolute
00425   // value of the outputs of one of them. So we only need to convolve
00426   // with the filter in two phases, then take the absolute value (i.e.,
00427   // we're doing |xox| and |xo|).
00428 
00429   Image<float> g0 = gaborFilter3(major_stddev, minor_stddev,
00430                                  filter_period, 0.0f, theta, size);
00431   Image<float> g90 = gaborFilter3(major_stddev, minor_stddev,
00432                                   filter_period, 90.0f, theta, size);
00433 
00434   LDEBUG("angle = %.2f, period = %.2f pix, size = %dx%d pix",
00435          angle, filter_period, g0.getWidth(), g0.getHeight());
00436 
00437   Image<float> f0, f90;
00438   ImageSet<float> result(gaussianPyr.size());
00439 
00440   for (uint i = 0; i < gaussianPyr.size(); ++i)
00441     {
00442       // if the i'th level in our input is empty, then leave it empty
00443       // in the output as well:
00444       if (!gaussianPyr[i].initialized())
00445         continue;
00446 
00447       if (flags & DO_ENERGY_NORM)
00448         {
00449           Image<float> temp = energyNorm(gaussianPyr[i]);
00450           f0 = optConvolve(temp, g0);
00451           f90 = optConvolve(temp, g90);
00452         }
00453       else
00454         {
00455           f0 = optConvolve(gaussianPyr[i], g0);
00456           f90 = optConvolve(gaussianPyr[i], g90);
00457         }
00458 
00459       if (!(flags & NO_ABS))
00460         {
00461           f0 = abs(f0);
00462           f90 = abs(f90);
00463         }
00464 
00465       // this really doesn't do much - take it out for now
00466       // DW 04/25/03
00467       //if (flags & DO_CLAMPED_DIFF)
00468       //{
00469       //  result[i] =
00470       //    clampedDiff(f0, gaussianPyr[i]) + clampedDiff(f90, gaussianPyr[i]);
00471       //}
00472       //else
00473       //{
00474       result[i] = f0 + f90;
00475       //}
00476     }
00477 
00478   return result;
00479 }
00480 
00481 // ######################################################################
00482 ImageSet<float> buildPyrGabor(const Image<float>& img,
00483                               int firstlevel, int depth, float angle,
00484                               float filter_period, float elongation,
00485                               int size, int flags)
00486 {
00487 GVX_TRACE(__PRETTY_FUNCTION__);
00488   ImageSet<float> pyr;
00489 
00490   //LINFO("flags: %d", flags);
00491   if (flags & DO_LAPLACIAN)
00492     {
00493       pyr = buildPyrLaplacian(img, firstlevel, depth, 9);
00494     }
00495   else
00496     {
00497       pyr = buildPyrGaussian(img, firstlevel, depth, 9);
00498     }
00499 
00500   return buildPyrGabor(pyr, angle, filter_period, elongation, size, flags);
00501 }
00502 
00503 // ######################################################################
00504 // ##### Processing Functions:
00505 // ######################################################################
00506 
00507 // ######################################################################
00508 template <class T>
00509 Image<T> centerSurround(const ImageSet<T>& pyr,
00510                         const int lev1, const int lev2,
00511                         const bool absol,
00512                         const ImageSet<float>* clipPyr)
00513 {
00514 GVX_TRACE(__PRETTY_FUNCTION__);
00515   ASSERT(lev1 >= 0 && lev2 >= 0);
00516   ASSERT(uint(lev1) < pyr.size() && uint(lev2) < pyr.size());
00517 
00518   const int largeLev = std::min(lev1, lev2);
00519   const int smallLev = std::max(lev1, lev2);
00520 
00521   if (clipPyr != 0 && clipPyr->isNonEmpty())
00522     {
00523       ASSERT((*clipPyr)[largeLev].getDims() == pyr[largeLev].getDims());
00524       ASSERT((*clipPyr)[smallLev].getDims() == pyr[smallLev].getDims());
00525 
00526       return
00527         ::centerSurround(Image<T>(pyr[largeLev] * (*clipPyr)[largeLev]),
00528                          Image<T>(pyr[smallLev] * (*clipPyr)[smallLev]),
00529                          absol);
00530     }
00531   else
00532     return ::centerSurround(pyr[largeLev], pyr[smallLev], absol);
00533 }
00534 
00535 // ######################################################################
00536 template <class T>
00537 void centerSurround(const ImageSet<T>& pyr,
00538                     const int lev1, const int lev2,
00539                     Image<T>& pos, Image<T>& neg,
00540                     const ImageSet<float>* clipPyr)
00541 {
00542 GVX_TRACE(__PRETTY_FUNCTION__);
00543   ASSERT(lev1 >= 0 && lev2 >= 0);
00544   ASSERT(uint(lev1) < pyr.size() && uint(lev2) < pyr.size());
00545 
00546   const int largeLev = std::min(lev1, lev2);
00547   const int smallLev = std::max(lev1, lev2);
00548 
00549   if (clipPyr != 0 && clipPyr->isNonEmpty())
00550     {
00551       ASSERT((*clipPyr)[largeLev].getDims() == pyr[largeLev].getDims());
00552       ASSERT((*clipPyr)[smallLev].getDims() == pyr[smallLev].getDims());
00553 
00554       ::centerSurround(Image<T>(pyr[largeLev] * (*clipPyr)[largeLev]),
00555                        Image<T>(pyr[smallLev] * (*clipPyr)[smallLev]),
00556                        pos, neg);
00557     }
00558   else
00559     ::centerSurround(pyr[largeLev], pyr[smallLev], pos, neg);
00560 }
00561 
00562 // ######################################################################
00563 template <class T>
00564 Image<T> centerSurroundSingleOpponent(const ImageSet<T>& cpyr,
00565                                       const ImageSet<T>& spyr,
00566                                       const int lev1, const int lev2,
00567                                       const bool absol,
00568                                       const ImageSet<float>* clipPyr)
00569 {
00570 GVX_TRACE(__PRETTY_FUNCTION__);
00571   ASSERT(lev1 >= 0 && lev2 >= 0);
00572   ASSERT(uint(lev1) < cpyr.size() && uint(lev2) < cpyr.size());
00573   ASSERT(uint(lev1) < spyr.size() && uint(lev2) < spyr.size());
00574 
00575   const int largeLev = std::min(lev1, lev2);
00576   const int smallLev = std::max(lev1, lev2);
00577 
00578   if (clipPyr != 0 && clipPyr->isNonEmpty())
00579     {
00580       ASSERT((*clipPyr)[largeLev].getDims() == cpyr[largeLev].getDims());
00581       ASSERT((*clipPyr)[smallLev].getDims() == cpyr[smallLev].getDims());
00582       ASSERT((*clipPyr)[largeLev].getDims() == spyr[largeLev].getDims());
00583       ASSERT((*clipPyr)[smallLev].getDims() == spyr[smallLev].getDims());
00584 
00585       return
00586         ::centerSurround(Image<T>(cpyr[largeLev] * (*clipPyr)[largeLev]),
00587                          Image<T>(spyr[smallLev] * (*clipPyr)[smallLev]),
00588                          absol);
00589     }
00590   else
00591     return ::centerSurround(cpyr[largeLev], spyr[smallLev], absol);
00592 }
00593 
00594 // ######################################################################
00595 template <class T>
00596 void centerSurroundSingleOpponent(const ImageSet<T>& cpyr,
00597                                   const ImageSet<T>& spyr,
00598                                   const int lev1, const int lev2,
00599                                   Image<T>& pos, Image<T>& neg,
00600                                   const ImageSet<float>* clipPyr)
00601 {
00602 GVX_TRACE(__PRETTY_FUNCTION__);
00603   ASSERT(lev1 >= 0 && lev2 >= 0);
00604   ASSERT(uint(lev1) < cpyr.size() && uint(lev2) < cpyr.size());
00605   ASSERT(uint(lev1) < spyr.size() && uint(lev2) < spyr.size());
00606 
00607   const int largeLev = std::min(lev1, lev2);
00608   const int smallLev = std::max(lev1, lev2);
00609 
00610   if (clipPyr != 0 && clipPyr->isNonEmpty())
00611     {
00612       ASSERT((*clipPyr)[largeLev].getDims() == cpyr[largeLev].getDims());
00613       ASSERT((*clipPyr)[smallLev].getDims() == cpyr[smallLev].getDims());
00614       ASSERT((*clipPyr)[largeLev].getDims() == spyr[largeLev].getDims());
00615       ASSERT((*clipPyr)[smallLev].getDims() == spyr[smallLev].getDims());
00616 
00617       ::centerSurround(Image<T>(cpyr[largeLev] * (*clipPyr)[largeLev]),
00618                        Image<T>(spyr[smallLev] * (*clipPyr)[smallLev]),
00619                        pos, neg);
00620     }
00621   else
00622     ::centerSurround(cpyr[largeLev], spyr[smallLev], pos, neg);
00623 }
00624 
00625 // ######################################################################
00626 template <class T>
00627 Image<T> centerSurroundDiff(const ImageSet<T>& pyr1,
00628                             const ImageSet<T>& pyr2,
00629                             const int lev1, const int lev2,
00630                             const bool absol,
00631                             const ImageSet<float>* clipPyr)
00632 {
00633 GVX_TRACE(__PRETTY_FUNCTION__);
00634   ASSERT(lev1 >= 0 && lev2 >= 0);
00635   ASSERT(uint(lev1) < pyr1.size() && uint(lev2) < pyr1.size());
00636 
00637   const int largeLev = std::min(lev1, lev2);
00638   const int smallLev = std::max(lev1, lev2);
00639 
00640   ASSERT(pyr1[largeLev].getDims() == pyr2[largeLev].getDims());
00641   ASSERT(pyr1[smallLev].getDims() == pyr2[smallLev].getDims());
00642 
00643   // compute differences between the two pyramids:
00644   const Image<T> limg = pyr1[largeLev] - pyr2[largeLev];
00645   const Image<T> simg = pyr1[smallLev] - pyr2[smallLev];
00646 
00647   if (clipPyr != 0 && clipPyr->isNonEmpty())
00648     {
00649       ASSERT((*clipPyr)[largeLev].getDims() == limg.getDims());
00650       ASSERT((*clipPyr)[smallLev].getDims() == simg.getDims());
00651 
00652       return ::centerSurround(Image<T>(limg * (*clipPyr)[largeLev]),
00653                               Image<T>(simg * (*clipPyr)[smallLev]),
00654                               absol);
00655     }
00656   else
00657     return ::centerSurround(limg, simg, absol);
00658 }
00659 
00660 // ######################################################################
00661 template <class T>
00662 void centerSurroundDiff(const ImageSet<T>& pyr1,
00663                         const ImageSet<T>& pyr2,
00664                         const int lev1, const int lev2,
00665                         Image<T>& pos, Image<T>& neg,
00666                         const ImageSet<float>* clipPyr)
00667 {
00668 GVX_TRACE(__PRETTY_FUNCTION__);
00669   ASSERT(lev1 >= 0 && lev2 >= 0);
00670   ASSERT(uint(lev1) < pyr1.size() && uint(lev2) < pyr1.size());
00671 
00672   const int largeLev = std::min(lev1, lev2);
00673   const int smallLev = std::max(lev1, lev2);
00674 
00675   ASSERT(pyr1[largeLev].getDims() == pyr2[largeLev].getDims());
00676   ASSERT(pyr1[smallLev].getDims() == pyr2[smallLev].getDims());
00677 
00678   // compute differences between the two pyramids:
00679   const Image<T> limg = pyr1[largeLev] - pyr2[largeLev];
00680   const Image<T> simg = pyr1[smallLev] - pyr2[smallLev];
00681 
00682   if (clipPyr != 0 && clipPyr->isNonEmpty())
00683     {
00684       ASSERT((*clipPyr)[largeLev].getDims() == limg.getDims());
00685       ASSERT((*clipPyr)[smallLev].getDims() == simg.getDims());
00686 
00687       ::centerSurround(Image<T>(limg * (*clipPyr)[largeLev]),
00688                        Image<T>(simg * (*clipPyr)[smallLev]),
00689                        pos, neg);
00690     }
00691   else
00692     ::centerSurround(limg, simg, pos, neg);
00693 }
00694 
00695 // ######################################################################
00696 template <class T>
00697 Image<T> centerSurroundDiffSingleOpponent(const ImageSet<T>& cpyr1,
00698                                           const ImageSet<T>& cpyr2,
00699                                           const ImageSet<T>& spyr1,
00700                                           const ImageSet<T>& spyr2,
00701                                           const int lev1, const int lev2,
00702                                           const bool absol,
00703                                           const ImageSet<float>* clipPyr)
00704 {
00705 GVX_TRACE(__PRETTY_FUNCTION__);
00706   ASSERT(lev1 >= 0 && lev2 >= 0);
00707   ASSERT(uint(lev1) < cpyr1.size() && uint(lev2) < cpyr1.size());
00708 
00709   const int largeLev = std::min(lev1, lev2);
00710   const int smallLev = std::max(lev1, lev2);
00711 
00712   ASSERT(cpyr1[largeLev].getDims() == cpyr2[largeLev].getDims());
00713   ASSERT(cpyr1[smallLev].getDims() == cpyr2[smallLev].getDims());
00714   ASSERT(spyr1[largeLev].getDims() == spyr2[largeLev].getDims());
00715   ASSERT(spyr1[smallLev].getDims() == spyr2[smallLev].getDims());
00716 
00717   // compute differences between the two pyramids:
00718   const Image<T> limg = cpyr1[largeLev] - cpyr2[largeLev];
00719   const Image<T> simg = spyr1[smallLev] - spyr2[smallLev];
00720 
00721   if (clipPyr != 0 && clipPyr->isNonEmpty())
00722     {
00723       ASSERT((*clipPyr)[largeLev].getDims() == limg.getDims());
00724       ASSERT((*clipPyr)[smallLev].getDims() == simg.getDims());
00725 
00726       return ::centerSurround(Image<T>(limg * (*clipPyr)[largeLev]),
00727                               Image<T>(simg * (*clipPyr)[smallLev]),
00728                               absol);
00729     }
00730   else
00731     return ::centerSurround(limg, simg, absol);
00732 }
00733 
00734 // ######################################################################
00735 template <class T>
00736 void centerSurroundDiffSingleOpponent(const ImageSet<T>& cpyr1,
00737                                       const ImageSet<T>& cpyr2,
00738                                       const ImageSet<T>& spyr1,
00739                                       const ImageSet<T>& spyr2,
00740                                       const int lev1, const int lev2,
00741                                       Image<T>& pos, Image<T>& neg,
00742                                       const ImageSet<float>* clipPyr)
00743 {
00744 GVX_TRACE(__PRETTY_FUNCTION__);
00745   ASSERT(lev1 >= 0 && lev2 >= 0);
00746   ASSERT(uint(lev1) < cpyr1.size() && uint(lev2) < cpyr1.size());
00747 
00748   const int largeLev = std::min(lev1, lev2);
00749   const int smallLev = std::max(lev1, lev2);
00750 
00751   ASSERT(cpyr1[largeLev].getDims() == cpyr2[largeLev].getDims());
00752   ASSERT(cpyr1[smallLev].getDims() == cpyr2[smallLev].getDims());
00753   ASSERT(spyr1[largeLev].getDims() == spyr2[largeLev].getDims());
00754   ASSERT(spyr1[smallLev].getDims() == spyr2[smallLev].getDims());
00755 
00756   // compute differences between the two pyramids:
00757   const Image<T> limg = cpyr1[largeLev] - cpyr2[largeLev];
00758   const Image<T> simg = spyr1[smallLev] - spyr2[smallLev];
00759 
00760   if (clipPyr != 0 && clipPyr->isNonEmpty())
00761     {
00762       ASSERT((*clipPyr)[largeLev].getDims() == limg.getDims());
00763       ASSERT((*clipPyr)[smallLev].getDims() == simg.getDims());
00764 
00765       ::centerSurround(Image<T>(limg * (*clipPyr)[largeLev]),
00766                        Image<T>(simg * (*clipPyr)[smallLev]),
00767                        pos, neg);
00768     }
00769   else
00770     ::centerSurround(limg, simg, pos, neg);
00771 }
00772 
00773 // ######################################################################
00774 template <class T>
00775 Image<T> weightedBlur(const Image<byte>& modulator, const ImageSet<T>& pyr)
00776 {
00777   // make sure params are correct:
00778   ASSERT(pyr.size() > 0);
00779   ASSERT(modulator.getDims() == pyr[0].getDims());
00780 
00781   // the modulator is 0 inside the objects and up to 255
00782   // outside.  We are now going to use the pyramid
00783   // to spatially modulate image resolution by picking a
00784   // pyramid level (with interpolation) according to the
00785   // modulator value. The code here is similar in spirit to
00786   // that in retina.C
00787   const int w = modulator.getWidth(), h = modulator.getHeight();
00788   Image<T> image(modulator.getDims(), NO_INIT);
00789   Image<byte>::const_iterator mod = modulator.begin();
00790   typename Image<T>::iterator ima = image.beginw();
00791   const int maxdepth = pyr.size() - 1;
00792   const float rstep = (float)(256 / maxdepth);
00793 
00794   for (int j = 0; j < h; j ++)
00795     for (int i = 0; i < w; i ++)
00796       {
00797         // determine resolution from which this pixel will be taken:
00798         const float d = (*mod++) / rstep;
00799 
00800         // get the pixel value from depth d, d+1:
00801         const int di = (int)(floor(d));
00802         const float dd = d - (float)di;
00803 
00804         const Point2D<int> loc(i, j);
00805         const T pix0 = getPyrPixel(pyr, loc, di);
00806         const T pix1 = getPyrPixel(pyr, loc, std::min(di+1, maxdepth));
00807         // This is fast but may cause clamping problems:
00808         //*ima++ = pix0 + (pix1 - pix0) * dd;
00809         // slower but more robust:
00810         *ima++ = clamped_convert<T>(pix0 * (1.0F - dd) + pix1 * dd);
00811       }
00812   return image;
00813 }
00814 
00815 // ######################################################################
00816 template <class T>
00817 Image<T> foveate(const Image<byte>& mask, const ImageSet<T>& pyr)
00818 {
00819   // make sure params are correct:
00820   ASSERT(mask.getDims() == pyr[0].getDims());
00821 
00822   // normalize the mask:
00823   Image<float> remask = mask;
00824   inplaceNormalize(remask, 0.0f, 255.0f);
00825   inplaceLowThresh(remask, 254.0f, 0.0f);
00826 
00827   // let's now tranform the mask into a distance map:
00828   const int w = remask.getWidth(), h = remask.getHeight();
00829   int maxdist = std::max(w, h) * 3;
00830   float scalefac = float(maxdist) / 255.0f;
00831   remask = chamfer34(remask, float(maxdist)) / scalefac;
00832   Image<byte> modulator = remask;  // will clamp as necessary
00833 
00834   // if modulator does not contain any point at zero (inside
00835   // object), that means that the mask was empty, which is
00836   // the case at the beginning of a simulation. Set it to some
00837   // intermediary value to provide a uniform medium blur:
00838   byte mi, ma; getMinMax(modulator, mi, ma);
00839   if (mi > 0) modulator /= 3;
00840 
00841   return weightedBlur(modulator, pyr);
00842 }
00843 
00844 // Include the explicit instantiations
00845 #include "inst/Image/PyramidOps.I"
00846 
00847 template ImageSet<double> buildPyrLocalAvg2x2(const Image<double>&, int);
00848 
00849 // ######################################################################
00850 /* So things look consistent in everyone's emacs... */
00851 /* Local Variables: */
00852 /* indent-tabs-mode: nil */
00853 /* End: */
Generated on Sun May 8 08:40:57 2011 for iLab Neuromorphic Vision Toolkit by  doxygen 1.6.3