Hmax.C

Go to the documentation of this file.
00001 /*!@file HMAX/Hmax.C Riesenhuber & Poggio's HMAX model for object recognition */
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/HMAX/Hmax.C $
00035 // $Id: Hmax.C 14376 2011-01-11 02:44:34Z pez $
00036 //
00037 
00038 #include "HMAX/Hmax.H"
00039 
00040 #include "Image/FilterOps.H" // for convolve() etc.
00041 #include "Image/Image.H"
00042 #include "Image/Kernels.H"   // for dogFilter()
00043 #include "Image/MathOps.H"
00044 #include "Util/MathFunctions.H"
00045 #include "Util/Types.H"
00046 #include "Util/log.H"
00047 #include "Util/safecopy.H"
00048 
00049 #include <cmath>
00050 #include <fstream>
00051 #include <iostream>
00052 #include <iomanip>
00053 #include <cstdio>
00054 #include <cstdlib>
00055 #include <limits>
00056 
00057 // ######################################################################
00058 Hmax::Hmax()
00059 { initialized = false; }
00060 
00061 // ######################################################################
00062 Hmax::Hmax(const int nori, const std::vector<int>& spacess,
00063            const std::vector<int>& scaless, const int c1spaceol,
00064            const bool angleflag, const float s2t, const float s2s,
00065            const float stdmin, const float stdstep,
00066            const int fsmin, const int fsstep)
00067 {
00068   initialized = false;
00069   init(nori, spacess, scaless, c1spaceol, angleflag, s2t, s2s);
00070   initFilters(stdmin,stdstep,fsmin,fsstep);
00071   initialized = true; 
00072 }
00073 
00074 // ######################################################################
00075 void Hmax::init(const int nori, const std::vector<int>& spacess,
00076                 const std::vector<int>& scaless, const int c1spaceol,
00077                 const bool angleflag, const float s2t, const float s2s)
00078 {
00079   freeMem(); ns = scaless[scaless.size() - 1]; no = nori;
00080   c1SpaceOL = c1spaceol; angleFlag = angleflag; s2Target = s2t; s2Sigma = s2s;
00081   spaceSS.resize(spacess.size()); scaleSS.resize(scaless.size());
00082 
00083  // detrmine number of scale bands from length of vector scaleSS:
00084   nsb = scaleSS.size() - 1;
00085 
00086   for (unsigned int i = 0; i < spacess.size(); i ++) spaceSS[i] = spacess[i];
00087   for (unsigned int i = 0; i < scaless.size(); i ++) scaleSS[i] = scaless[i];
00088 
00089 }
00090 
00091 void Hmax::initFilters(const float stdmin, const float stdstep, const int fsmin, const int fsstep)
00092 {
00093   // create the filters:
00094   typedef Image<float>* FloatImagePtr;
00095   filter = new FloatImagePtr[ns];
00096   for(int s = 0; s < ns; s++)
00097     {
00098       filter[s] = new Image<float>[no];
00099       for(int o = 0; o < no; o ++)
00100         {
00101           // create DoG filter:
00102           filter[s][o] = dogFilter<float>(stdmin + stdstep * s,
00103                                           (float)o * 180.0F / (float)no,
00104                                           fsmin + fsstep * s);
00105           // normalize to zero mean:
00106           filter[s][o] -= mean(filter[s][o]);
00107 
00108           // normalize to unit of sum-of-squares:
00109           filter[s][o] /= sum(squared(filter[s][o]));
00110         }
00111     }
00112 }
00113 
00114 int Hmax::getNumOrientations()
00115 {
00116   return no;
00117 }
00118 
00119 // ######################################################################
00120 void Hmax::freeMem()
00121 {
00122   if (initialized)
00123     {
00124       initialized = false;
00125       for(int s = 0; s < ns; s++) delete [] filter[s];
00126       delete [] filter;
00127     }
00128 }
00129 
00130 // ######################################################################
00131 Hmax::~Hmax()
00132 { freeMem(); }
00133 
00134 // ######################################################################
00135 std::vector<std::string> Hmax::readDir(std::string inName)
00136 {
00137         DIR *dp = opendir(inName.c_str());
00138         if(dp == NULL)
00139         {
00140           LFATAL("Directory does not exist %s",inName.c_str());
00141         }
00142         dirent *dirp;
00143         std::vector<std::string> fList;
00144         while ((dirp = readdir(dp)) != NULL ) {
00145                 if (dirp->d_name[0] != '.')
00146                         fList.push_back(inName + '/' + std::string(dirp->d_name));
00147         }
00148         LINFO("%"ZU" files in the directory\n", fList.size());
00149         LINFO("file list : \n");
00150         for (unsigned int i=0; i<fList.size(); i++)
00151                 LINFO("\t%s", fList[i].c_str());
00152         std::sort(fList.begin(),fList.end());
00153         return fList;
00154 }
00155 
00156 // ######################################################################
00157 std::vector<std::string> Hmax::readList(std::string inName)
00158 {
00159   std::ifstream inFile;
00160   inFile.open(inName.c_str(),std::ios::in);
00161   if(!inFile){
00162     LFATAL("Unable to open image path list file: %s",inName.c_str());
00163   }
00164   std::string sLine;
00165   std::vector<std::string> fList;
00166   while (std::getline(inFile, sLine)) {
00167       std::cout << sLine << std::endl;
00168       fList.push_back(sLine);
00169   }
00170   LINFO("%"ZU" paths in the file\n", fList.size());
00171   LINFO("file list : \n");
00172   for (unsigned int i=0; i<fList.size(); i++)
00173     LINFO("\t%s", fList[i].c_str());
00174   inFile.close();
00175   return fList;
00176 }
00177 
00178 
00179 // ######################################################################
00180 // This is the code of Max Riesenhuber almost straight out of the box.
00181 // Minor modifications have been made, to:
00182 // - use float rather than double throughout
00183 // - interface with our Image class rather than Matlab matrices
00184 // - limit lines of code at 80 chars
00185 // - allocate memory through malloc rather than Matlab functions
00186 // - use Image<float> rather than Matlab matrices for result
00187 // - changed fSiz, spaceSS, scaleSS from float to int (avoid compiler warnings)
00188 // - made all pointers to input parameters pointers to const data
00189 // - switched scaleSS to zero-based rather than Matlab's one-based
00190 // - switched to using our built-in array of filters
00191 Image<float> Hmax::origGetC2(const Image<float>& input)
00192 {
00193   int offTab[8]={0,0,0,-1,-1,0,-1,-1};
00194   int imgy,imgx;
00195   int i,j,k,x,y,bufX,bufY;
00196   float *buf,*bStart,*currB;
00197   int fx,fy;
00198   float res;
00199   float imgLen;
00200   float *retPtr,*rPtr2;
00201   Image<float> retC2;
00202   float *ret,*c2Ptr;
00203   float *sBuf,*sBufPtr,*sPtr2;
00204   float *c1Act,s2Resp;
00205   int maxFS,sSS,scaleInd,numScaleBands,numSimpleFilters,numPos;
00206   int currScale,yS,xS,sFInd;
00207   int f1,f2,f3,f4,c2Ind,maxXY;
00208   int poolRange;
00209 
00210   Image<float>::const_iterator image = input.begin();
00211   imgy = input.getHeight();
00212   imgx = input.getWidth();
00213 
00214   maxFS = filter[ns - 1][0].getWidth(); // LI: use our built-in filters
00215   //for(maxFS=0,i=fSizY*fSizX-1;i>=0;i--) /* get maximum filter size */
00216   //  maxFS=fSiz[i]>maxFS?fSiz[i]:maxFS;
00217 
00218   /* for each interval in scaleSS, filter type, go through spaceSS,
00219      allocate enough mem to calc all these response fields in S1 &
00220      then do max over the required range */
00221   numScaleBands=scaleSS.size()-1; /* convention: last element in
00222                                         c1ScaleSS is max index + 1 */
00223 
00224   //  numScales=scaleSS[numScaleBands];
00225   /* last index in scaleSS contains scale index where next band would
00226      start, i.e., 1 after highest scale!! */
00227 
00228   numSimpleFilters = no; // LI: use our built-in filters
00229   // numSimpleFilters=fSizY*fSizX/numScales;
00230 
00231   /* calculate number of positions for each C-cell */
00232   /* base number of positions on smallest pooling range */
00233   numPos=(int)(ceil((double)(imgy/spaceSS[0]))*ceil((double)(imgx/spaceSS[0])))*
00234     c1SpaceOL*c1SpaceOL;
00235 
00236   /* be a little wasteful: in each filter band, allocate enough for
00237      smallest scale */
00238   retC2.resize(numSimpleFilters*numSimpleFilters,
00239                numSimpleFilters*numSimpleFilters);
00240   c2Ptr=retC2.getArrayPtr();
00241   /* allocate memory for C1 activations */
00242   ret=(float*)malloc(numPos*numScaleBands*numSimpleFilters*sizeof(float));
00243 
00244  /* s1 activations before pooling over space (sBuf already pools over
00245     *scale*) */
00246   sBuf=(float*)malloc(numSimpleFilters*imgy*imgx*sizeof(float));
00247 
00248   /* buf is buffer to perform convolutions in (with zero padding) */
00249   bufX=imgx+maxFS;
00250   bufY=imgy+maxFS;
00251   buf=(float*)malloc(bufX*bufY*sizeof(float));
00252 
00253   /* copy image and pad with zeros to half max filter size */
00254   memset(buf,0,bufX*bufY*sizeof(float));
00255   Image<float>::const_iterator currI = image;
00256   for(currB=buf+(maxFS>>1)*bufX+(maxFS>>1),i=0;i<imgy;i++,
00257         currI+=imgx,currB+=bufX)
00258     safecopy(currB,currI,imgx);
00259 
00260   for(scaleInd=0;scaleInd<numScaleBands;scaleInd++){
00261     memset(sBuf,0,numSimpleFilters*imgy*imgx*sizeof(float));
00262     for(currScale=scaleSS[scaleInd];currScale<scaleSS[scaleInd+1];currScale++){
00263       for(sBufPtr=sBuf,sFInd=0;sFInd<numSimpleFilters;sFInd++){
00264         fx = filter[currScale][sFInd].getWidth();
00265         fy = filter[currScale][sFInd].getHeight();
00266         for(y=0,bStart=buf+(maxFS>>1)*bufX+(maxFS>>1);y<imgy;y++,bStart+=
00267               bufX-imgx){
00268           for(x=0;x<imgx;x++,bStart++,sBufPtr++){
00269             /* center filter on current image point */
00270             Image<float>::const_iterator currF =
00271               filter[currScale][sFInd].begin();
00272             for(res=0,imgLen=0,currB=bStart-fy/2*bufX-fx/2,
00273                   j=0;j<fy;j++,currB+=bufX-fx)
00274               for(k=0;k<fx;k++){
00275                 imgLen+=*currB**currB;
00276                 res += *currB++**currF++;
00277               }
00278             if(angleFlag && (imgLen>0)) res/=sqrt(imgLen);
00279             res=fabs(res);
00280             *sBufPtr = *sBufPtr>res?*sBufPtr:res; /*already do max over scale*/
00281           }
00282         }
00283       }
00284     }
00285 
00286     /* now pool over space, take overlap into account */
00287     /* take ceiling here otherwise might get more than c1SpaceOL times */
00288     for(retPtr=ret+numPos*numSimpleFilters*scaleInd,sSS=(int)
00289           ceil((float)spaceSS[scaleInd]/c1SpaceOL),
00290           poolRange=spaceSS[scaleInd],sFInd=0;sFInd<numSimpleFilters;sFInd++)
00291       for(rPtr2=retPtr+numPos*sFInd,yS=0;yS<imgy;yS+=sSS)
00292         for(xS=0;xS<imgx;xS+=sSS,rPtr2++)
00293           /* eee still have same pooling range!!
00294              division by c1SpaceOL only in stepping of start pos! */
00295           for(*rPtr2=0.0,sBufPtr=sBuf+imgx*(imgy*sFInd+yS)+xS,y=yS;
00296               (y-yS<poolRange)&&(y<imgy);y++)
00297             for(sPtr2=sBufPtr+(y-yS)*imgx,x=xS;(x-xS<poolRange)&&(x<imgx);
00298                 x++,sPtr2++)
00299               *rPtr2=*rPtr2>*sPtr2?*rPtr2:*sPtr2;
00300   }
00301 
00302   /* now: do S2 calculation by doing all combinations of features  */
00303   /* to make things a little more efficient, the outer loop runs over
00304      the 4 filters that a S2 cell combines, the inner loop does the calculation
00305      for all pos & filter bands & takes the max (ret contains C2 then w/out
00306      exp) */
00307   for(c1Act=ret,c2Ind=0,f1=0;f1<numSimpleFilters;f1++)
00308     for(f2=0;f2<numSimpleFilters;f2++)
00309       for(f3=0;f3<numSimpleFilters;f3++)
00310         for(f4=0;f4<numSimpleFilters;f4++,c2Ind++){
00311           for(c2Ptr[c2Ind]=res=-1e10,scaleInd=0;scaleInd<numScaleBands;
00312               scaleInd++){
00313             /* eee assume square image */
00314             for(maxXY=(int)ceil(imgy/ceil((float)
00315                                           spaceSS[scaleInd]/c1SpaceOL)),
00316                   y=c1SpaceOL;y<maxXY;y++)
00317               for(x=c1SpaceOL;x<maxXY;x++){
00318                 /* use the fact that exp is monotonous in abs(arg):
00319                    just pass back max of neg. dist (arg of exp) */
00320                 s2Resp=squareOf<float>(c1Act[numPos*(scaleInd*numSimpleFilters+f1)+
00321                                 y+maxXY*x]-s2Target)+
00322                   squareOf<float>(c1Act[numPos*(scaleInd*numSimpleFilters+f2)+y+
00323                            c1SpaceOL*offTab[2]+
00324                            maxXY*(x+c1SpaceOL*offTab[3])]-s2Target)+
00325                   squareOf<float>(c1Act[numPos*(scaleInd*numSimpleFilters+f3)+y+
00326                            c1SpaceOL*offTab[4]+
00327                            maxXY*(x+c1SpaceOL*offTab[5])]-s2Target)+
00328                   squareOf<float>(c1Act[numPos*(scaleInd*numSimpleFilters+f4)+y+
00329                            c1SpaceOL*offTab[6]+
00330                            maxXY*(x+c1SpaceOL*offTab[7])]-s2Target);
00331                 res=s2Resp>res?s2Resp:res;
00332               }
00333             c2Ptr[c2Ind]=c2Ptr[c2Ind]>res?c2Ptr[c2Ind]:res; /*max over scale*/
00334           }
00335         }
00336     free(sBuf);
00337     free(buf);
00338     free(ret);
00339     return hmaxActivation(retC2, s2Sigma);
00340 }
00341 
00342 
00343 // ######################################################################
00344 void Hmax::getC1(const Image<float>& input, Image<float>** &c1Res)
00345 {
00346   Image<float> *c1Buff = new Image<float>[no];
00347   Image<float> s1Res;
00348   // loop over scale bands:
00349 
00350 
00351   for(int sb = 0; sb < nsb; sb ++) {
00352 
00353     // clear our buffers:
00354     for(int i = 0; i < no; i ++)
00355       c1Buff[i].resize(input.getWidth(), input.getHeight(), true);;
00356 
00357     // loop over scales within current scale band:
00358     for(int s = scaleSS[sb]; s < scaleSS[sb + 1]; s++) {
00359       // loop over orientations:
00360       for(int o = 0; o < no; o ++) {
00361         // convolve image by filter at current orient & scale:
00362         if (angleFlag) {
00363           s1Res = convolveHmax(input, filter[s][o]); // normalize by image energy
00364           //printCorners("s1Res",s1Res,s==scaleSS[0]&&sb==0&&o==0);
00365         }
00366         else {
00367           s1Res = convolve(input, filter[s][o], CONV_BOUNDARY_CLEAN); // no normalization
00368           // take absolute value of the convolution result:
00369           s1Res = abs(s1Res);
00370         }
00371 
00372         // take max between this convolution and previous ones for
00373         // that orientation but other scales within current scale band:
00374         c1Buff[o] = takeMax<float>(c1Buff[o], s1Res);
00375       }
00376     }
00377 
00378     // compute RF spacing (c1R) and pool range (c1PR):
00379     int c1R = (int)ceil((float)spaceSS[sb] / (float)c1SpaceOL);
00380     int c1PR = spaceSS[sb];
00381 
00382     // pool over space for each orientation (and scale band):
00383     for(int o = 0; o < no; o ++){
00384       c1Res[sb][o] = spatialPoolMax(c1Buff[o], c1R, c1R, c1PR, c1PR);
00385       //printCorners("c1Res",c1Res[sb][o],sb==0&&o==0);
00386     }
00387 
00388   }
00389 
00390   delete [] c1Buff;
00391 }
00392 
00393 void Hmax::initC1(Image<float> **&c1Res)
00394 {
00395   c1Res = new Image<float>*[nsb];
00396   for (int sb = 0; sb < nsb; sb ++) c1Res[sb] = new Image<float>[no];
00397 }
00398 
00399 void Hmax::clearC1(Image<float> **&c1Res)
00400 {
00401    for (int sb = 0; sb < nsb; sb ++) delete [] c1Res[sb];
00402    delete [] c1Res;
00403 }
00404 
00405 void Hmax::printCorners(const char name[], const Image<float>& im, bool cond)
00406 {
00407   if(cond) {
00408     printf("%s\n",name);
00409     int w = im.getWidth();
00410     int h = im.getHeight();
00411     /*
00412     std::string s;
00413     if(w>2 && h>2) {
00414       printf("%f\t%f\t%f\t%f\t%f\n",im.getVal(0,0),im.getVal(1,0),im.getVal(2,0),im.getVal(w-2,0),im.getVal(w-1,0));
00415       printf("%f\t%f\t\t\t%f\t%f\n\n", im.getVal(0,1),im.getVal(1,1),im.getVal(w-2,1),im.getVal(w-1,1));
00416       printf("%f\t%f\t\t\t%f\t%f\n", im.getVal(0,h-2),im.getVal(1,h-2),im.getVal(w-2,h-2),im.getVal(w-1,h-2));
00417       printf("%f\t%f\t%f\t%f\t%f\n",im.getVal(0,h-1),im.getVal(1,h-1),im.getVal(2,h-1),im.getVal(w-2,h-1),im.getVal(w-1,h-1));
00418     }
00419     else if(w>1 && h>1) {
00420       printf("%f\t%f\n",im.getVal(0,0),im.getVal(1,0));
00421       printf("%f\t%f\n", im.getVal(0,1),im.getVal(1,1));
00422     }
00423     else if(w>0 && h>0){
00424       printf("%f\n",im.getVal(0,0));
00425     }
00426     */
00427     std::cout << "Mean of " << name << " " << mean(im) << std::endl;
00428     std::cout << "Var of " << name << " " << (stdev(im)*stdev(im)) << std::endl;
00429     std::cout << "Width of " << w << " and height of " << h << std::endl;
00430     //float mi,ma; getMinMax(input,mi,ma);
00431     //writeOutImage(inputf,name);
00432     //std::cout << "Min " << mi << " Max " << ma << std::endl;
00433   }
00434 }
00435 
00436 void Hmax::writeOutImage(const Image<float>& im,std::string & fName)
00437 {
00438   std::ofstream oFile;
00439   Image<float> d;
00440   oFile.open(fName.c_str(),std::ios::out);
00441   d = im;
00442   int w,h;
00443   w = d.getWidth();
00444   h = d.getHeight();
00445   for(int i=0;i<w;i++){
00446     for(int j=0;j<h;j++){
00447       oFile << d.getVal(i,j) <<" ";
00448     }
00449     if(i!=w-1)
00450       oFile << std::endl;
00451   }
00452   oFile.close();
00453 
00454 }
00455 
00456 
00457 // ######################################################################
00458 Image<float> Hmax::getC2(const Image<float>& input)
00459 {
00460   // detrmine number of scale bands from length of vector scaleSS:
00461   int nsb = scaleSS.size() - 1;
00462 
00463   // allocate buffers for intermediary results:
00464   Image<float> **c1Res;
00465   initC1(c1Res);
00466 
00467   // ******************************
00468   // *** Compute S1/C1 output:
00469   // ******************************
00470   getC1(input, c1Res);
00471 
00472   // ******************************
00473   // *** Compute S2/C2 output:
00474   // ******************************
00475   Image<float> c2Res(no * no, no * no, NO_INIT);
00476   c2Res.clear(-1.0E10F);
00477   int idx = 0;
00478 
00479   // loop over four filters giving inputs to an S2 cell:
00480   for (int f1 = 0; f1 < no; f1++)
00481     for (int f2 = 0; f2 < no; f2++)
00482       for (int f3 = 0; f3 < no; f3++)
00483         for (int f4 = 0; f4 < no; f4++) {
00484 
00485           float c2r = -1.0E10;
00486           // loop over scale band:
00487           for (int sb = 0; sb < nsb; sb ++) {
00488 
00489             float s2r = featurePoolHmax(c1Res[sb][f1], c1Res[sb][f2],
00490                                         c1Res[sb][f3], c1Res[sb][f4],
00491                                         c1SpaceOL, c1SpaceOL, s2Target);
00492             if (s2r > c2r) c2r = s2r;
00493           }
00494           c2Res.setVal(idx, c2r); idx ++;
00495         }
00496 
00497   // free allocated temporary images:
00498   clearC1(c1Res);
00499 
00500   return hmaxActivation(c2Res, s2Sigma);
00501 }
00502 
00503 void Hmax::sumFilter(const Image<float>& image, const float radius, Image<float>& newImage)
00504 {
00505   Rectangle sumSupport = Rectangle::tlbrI(0,0,int(radius*2.0F),int(radius*2.0F));
00506   sumFilter(image,sumSupport,newImage);
00507 }
00508 
00509 void Hmax::sumFilter(const Image<float>& image, const Rectangle& support, Image<float>& newImage)
00510 {
00511 
00512   Dims d(support.top()+support.bottomI()+1,support.left()+support.rightI()+1);
00513   Image<float> a(d,NO_INIT);
00514   a.clear(1.0F);
00515 
00516   //convolve the image with a matrix of 1's that is as big as the rectangle
00517   // This two step process is doing effectively the same thing by taking the center part of the convolution
00518   //I2 = conv2(ones(1,radius(2)+radius(4)+1), ones(radius(1)+radius(3)+1,1), I);
00519   //I3 = I2((radius(4)+1:radius(4)+size(I,1)), (radius(3)+1:radius(3)+size(I,2)));
00520   //Image<float> i;
00521   //i = convolution(image,a,MATLAB_STYLE_CONVOLUTION);
00522   //Rectangle r = Rectangle::tlbrI(support.bottomI()+1,support.rightI()+1,support.bottomI()+image.getHeight(),support.rightI()+image.getWidth());
00523   //newImage = crop(i,r);
00524   // Can be done in one step
00525   newImage = convolve(image,a,CONV_BOUNDARY_ZERO);
00526 }
00527 
00528 
00529 // ######################################################################
00530 /* So things look consistent in everyone's emacs... */
00531 /* Local Variables: */
00532 /* indent-tabs-mode: nil */
00533 /* End: */
Generated on Sun May 8 08:04:48 2011 for iLab Neuromorphic Vision Toolkit by  doxygen 1.6.3