BeoSubCanny.C

Go to the documentation of this file.
00001 /*!@file BeoSub/BeoSubCanny.C vision for the sub */
00002 
00003 // //////////////////////////////////////////////////////////////////// //
00004 // The iLab Neuromorphic Vision C++ Toolkit - Copyright (C) 2000-2003   //
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: Zack Gossman <gossman@usc.edu>
00034 // $HeadURL: svn://isvn.usc.edu/software/invt/trunk/saliency/src/BeoSub/BeoSubCanny.C $
00035 // $Id: BeoSubCanny.C 14376 2011-01-11 02:44:34Z pez $
00036 //
00037 
00038 #include "BeoSub/BeoSubCanny.H"
00039 
00040 #include "Image/MathOps.H"
00041 #include "Image/MorphOps.H"     // for erodeImg(), closeImg()
00042 #include "Image/Transforms.H"
00043 #include "Util/MathFunctions.H" // for squareOf() inline function
00044 
00045 #include <algorithm> // for std::max()
00046 
00047 // ######################################################################
00048 BeoSubCanny::BeoSubCanny(OptionManager& mgr,
00049                          const std::string& descrName,
00050                          const std::string& tagName):
00051   ModelComponent(mgr, descrName, tagName)
00052 {
00053   //set math details for blur and threshold and such. For now, harcoded
00054   sigma = .5;
00055   tlow = .5;
00056   thigh = .5;
00057 
00058   color.resize(4, 0.0F);
00059 
00060   hasSetup = false;
00061   debugmode = true;
00062 }
00063 
00064 // ######################################################################
00065 //FIX to accept color as an array rather than a char arg
00066 void BeoSubCanny::setupCanny(const char* colorArg, Image< PixRGB<byte> > image, bool debug)
00067 {
00068 
00069   bool useColor = true;
00070 
00071   debugmode = debug;//turns output on or off
00072 
00073   if(!hasSetup){
00074     segmenter = new segmentImageTrackMC<float,unsigned int,4> (image.getWidth()*image.getHeight());
00075     int wif = image.getWidth()/4;
00076     int hif = image.getHeight()/4;
00077     segmenter->SITsetFrame(&wif,&hif);
00078     // Set display colors for output of tracking. Strictly aesthetic //TEST
00079     //And definitely not needed, although for some reason it's required --Z
00080     segmenter->SITsetCircleColor(0,255,0);
00081     segmenter->SITsetBoxColor(255,255,0,0,255,255);
00082 
00083     segmenter->SITsetUseSmoothing(true,10);
00084     //method provided by Nathan to turn off adaptation (useful for dynamic environments)
00085     segmenter->SITtoggleColorAdaptation(false);
00086     //enable single-frame tracking
00087     segmenter->SITtoggleCandidateBandPass(false);
00088   }
00089 
00090 
00091   //COLORTRACKING DECS: note that much of these may not need to be in this file. Instead, the module could accept an array describing the color.
00092 
00093   //Colors MUST use H2SV2 pixel values! use test-sampleH2SV2 to sample needed values! --Z
00094 
00095   //NOTE: These color presets are extremely dependent on the camera hardware settings. It seems to be best to manually set the white blance and other settings.
00096  //0 = H1 (0-1), 1=H2 (0-1), 2=S (0-1), 3=V (0-1)
00097   if(!strcmp(colorArg, "Red")){
00098     //RED
00099     color[0] = 0.76700F; color[1] = 0.85300F; color[2] = 0.80000F; color[3] = 0.97200F;
00100   }
00101 
00102   else if(!strcmp(colorArg, "Green")){
00103     //Green
00104     color[0] = 0.53000F; color[1] = 0.36000F; color[2] = 0.44600F; color[3] = 0.92000F;
00105   }
00106 
00107   else if(!strcmp(colorArg, "Orange")){
00108     //"ORANGE"
00109     color[0] = 0.80000F; color[1] = 0.80000F; color[2] = 0.55000F; color[3] = 1.00000F;
00110   }
00111   else if(!strcmp(colorArg, "Blue")){
00112     //BLUE
00113     color[0] = 0.27500F; color[1] = 0.52500F; color[2] = 0.44500F; color[3] = 0.97200F;
00114   }
00115   else if(!strcmp(colorArg, "Yellow")){
00116     //YELLOW
00117     color[0] = 0.89000F; color[1] = 0.65000F; color[2] = 0.90000F; color[3] = 0.97000F;
00118   }
00119   else if(!strcmp(colorArg, "White")){
00120     color[0] = 0.70000F; color[1] = 0.70000F; color[2] = 0.04000F; color[3] = 1.00000F;
00121   }
00122   else if(!strcmp(colorArg, "Black")){
00123     color[0] = 0.60001F; color[1] = 0.66000F; color[2] = 0.24000F; color[3] = 0.70000F;
00124   }
00125   else{
00126     useColor = false;
00127   }
00128 
00129   //! +/- tollerance value on mean for track
00130   std::vector<float> std(4,0.0F);
00131   //NOTE that the saturation tolerance is important (if it goes any higher than this, it will nearly always recognize white!)
00132   std[0] = 0.30000; std[1] = 0.30000; std[2] = 0.34500; std[3] = 0.15000;//3 was .45, 2 was .445
00133 
00134   if(!strcmp(colorArg, "White") || !strcmp(colorArg, "Black")){
00135     std[0] = 1.0F; std[1] = 1.0F; std[2] = 0.1F; std[3] = 0.1F;
00136 
00137    }
00138 
00139    //! normalizer over color values (highest value possible)
00140    std::vector<float> norm(4,0.0F);
00141    norm[0] = 1.0F; norm[1] = 1.0F; norm[2] = 1.0F; norm[3] = 1.0F;
00142 
00143    //! how many standard deviations out to adapt, higher means less bias
00144    //The lower these are, the more strict recognition will be in subsequent frames
00145    //TESTED AND PROVED do NOT change unless you're SURE
00146    std::vector<float> adapt(4,0.0F);
00147    //adapt[0] = 3.5F; adapt[1] = 3.5F; adapt[2] = 3.5F; adapt[3] = 3.5F;
00148    adapt[0] = 5.0F; adapt[1] = 5.0F; adapt[2] = 5.0F; adapt[3] = 5.0F;
00149 
00150    if(!strcmp(colorArg, "White") || !strcmp(colorArg, "Black")){
00151      adapt[0] = 25.0F; adapt[1] = 25.0F; adapt[2] = 0.1F; adapt[3] = 0.1F;
00152 
00153    }
00154 
00155    //! highest value for color adaptation possible (hard boundry)
00156    std::vector<float> upperBound(4,0.0F);
00157    upperBound[0] = color[0] + 0.45F; upperBound[1] = color[1] + 0.45F; upperBound[2] = color[2] + 0.55F; upperBound[3] = color[3] + 0.55F;
00158 
00159    //! lowest value for color adaptation possible (hard boundry)
00160    std::vector<float> lowerBound(4,0.0F);
00161    lowerBound[0] = color[0] - 0.45F; lowerBound[1] = color[1] - 0.45F; lowerBound[2] = color[2] - 0.55F; lowerBound[3] = color[3] - 0.55F;
00162    //END DECS
00163 
00164    colorImg = image;
00165    imgW = colorImg.getWidth();
00166    imgH = colorImg.getHeight();
00167 
00168    //color tracking stuff
00169    segmenter->SITsetTrackColor(&color,&std,&norm,&adapt,&upperBound,&lowerBound);
00170 
00171    ww = imgW/4;
00172    hh = imgH/4;
00173 
00174    Image<PixRGB<byte> > Aux;
00175    Aux.resize(100,450,true);
00176    Image<byte> temp;
00177    Image< PixRGB<byte> > display = colorImg;
00178 
00179    if(debugmode){
00180      if(!win.get()){
00181        win.reset( new XWindow(colorImg.getDims(), -1, -1, "test-canny window") );
00182        win->setPosition(0, 0);
00183      }
00184      win->drawImage(colorImg);
00185    }
00186 
00187    //Perform color exclusion, if desired
00188    if(useColor){
00189 
00190      //Get candidate image representing blobs of desired color
00191      H2SVimage = colorImg;//changed since transfer to H2SV
00192      display = colorImg;
00193      segmenter->SITtrackImageAny(H2SVimage,&display,&Aux,true);
00194      temp = segmenter->SITreturnCandidateImage();//get image we'll be using for edge detection
00195      //NOTE: This line works off of a "weeded-out" version of the blob tracker. It may be best to switch to this once the colors are set VERY well. Unfortunately, it is picky enough about colors to make the color settings difficult to determine. FIX?
00196      //temp = (Image<byte>)(segmenter->SITreturnBlobMap());
00197 
00198      //set up structuring elements (disk)
00199      Image<byte> se1(5, 5, ZEROS);
00200      Point2D<int> t1(2,2);
00201      drawDisk(se1, t1, 2, static_cast<byte>(255));
00202 
00203      Image<byte> se2(9, 9, ZEROS);
00204      Point2D<int> t2(4,4);
00205      drawDisk(se2, t2, 3, static_cast<byte>(255));
00206 
00207      //Erode image to prevent noise NOTE: isn't working well, erodes too much, probably because temp is so lowrez
00208      //temp = erodeImg(temp, se1);
00209 
00210      //Close candidate image to prevent holes
00211      //temp = closeImg(temp, se2);
00212      /*//the following is to exclude non-surrounded colors. was used for the bin
00213      // flood from corners:
00214      Image<byte> temp2 = binaryReverse(temp, byte(255));
00215      floodClean(temp, temp2, Point2D<int>(0, 0), byte(255), byte(0));
00216 
00217      temp = temp2;
00218      floodClean(temp2, temp, Point2D<int>(temp.getWidth()-1, temp.getHeight()-1), byte(255), byte(0));
00219 
00220      temp = binaryReverse(temp, byte(255));
00221      */
00222      grayImg = (Image<byte>)(quickInterpolate(temp,4));//note that this is very low rez
00223    }
00224    else{
00225      grayImg = luminance(colorImg);
00226    }
00227 
00228    hasSetup = true;
00229 }
00230 
00231 // ######################################################################
00232 bool BeoSubCanny::runCanny(rutz::shared_ptr<ShapeModel>& shapeArg){
00233 
00234 
00235   //Perform canny edge detection
00236   canny(grayImg.getArrayPtr(), grayImg.getHeight(), grayImg.getWidth(), sigma, tlow, thigh, &edge, NULL);
00237 
00238   Image<unsigned char> cannyCharImg(edge, grayImg.getWidth(), grayImg.getHeight());
00239 
00240   Image<float> cannyImg = cannyCharImg;
00241 
00242 
00243   //Raster::WriteGray(cannyImg, outfilename, RASFMT_PNM);//edge image
00244   if(debugmode){
00245     if(!xwin.get()){
00246       xwin.reset(new XWindow(colorImg.getDims()));
00247       xwin->setPosition(colorImg.getWidth()+10, 0);
00248     }
00249     xwin->drawImage((Image< PixRGB<byte> >)grayImg);
00250   }
00251 
00252   centroid(cannyCharImg);
00253   distMap = chamfer34(cannyImg, 5000.0f);
00254 
00255   //perform shape detection
00256   //get initial dimensions for the shape
00257 
00258   bool shapeFound = false;
00259   double** xi = (double**)calloc(6, sizeof(double));
00260 
00261 
00262    //NOTE: HERE is where the initial direction matrix is built. Change these values to change axis assignment
00263    for(int i=0; i<6; i++) {
00264      xi[i] = (double*)calloc(6, sizeof(double));
00265      xi[i][i] = 1.0;
00266    }
00267    //Reset initial axes
00268    xi[0][0] = 0.5;
00269    xi[0][1] = 0.5;
00270 
00271   int iter = 0;
00272   double fret = 0.0;
00273 
00274   //here, we will be using powell classes, and, instead of passing in calcDist, will be passing in a shape.
00275    shapeFound = powell(shapeArg->getDimensions(), xi, shapeArg->getNumDims(), 0.5, &iter, &fret, shapeArg);
00276 
00277    if(debugmode){
00278      double* p = shapeArg->getDimensions();//TEST
00279      if(shapeFound){
00280        printf("\n\nShape found!\n");
00281        printf("Final dimensions are -- ");
00282        for(int w = 1; w <=shapeArg->getNumDims(); w++){
00283          printf("%d: %f ", w, p[w]);//outputs final values
00284        }
00285        printf("Certainty (lower is better): %f\n\n", fret);
00286      }else{
00287        printf("\n\nShape not found. Certainty was %f\n\n", fret);
00288      }
00289      }
00290 
00291    return(shapeFound);
00292 }
00293 
00294 
00295 
00296 // ######################################################################
00297 BeoSubCanny::~BeoSubCanny()
00298 {
00299 }
00300 
00301 ///////////////////////FROM NRUTIL/////////////////////////
00302 // ######################################################################
00303 double *BeoSubCanny::nrVector(long nl, long nh)
00304 /* allocate a double vector with subscript range v[nl..nh] */
00305 {
00306         double *v;
00307 
00308         v=(double *)malloc((size_t) ((nh-nl+2)*sizeof(double)));
00309         if (!v) printf("allocation failure in nrVector()");
00310         return v-nl+1;
00311 }
00312 
00313 // ######################################################################
00314 void BeoSubCanny::free_nrVector(double *v, long nl, long nh)
00315 /* free a double vector allocated with nrVector() */
00316 {
00317         free((FREE_ARG) (v+nl-1));
00318 }
00319 
00320 
00321 // ######################################################################
00322 void BeoSubCanny::mnbrak(double *ax, double *bx, double *cx, double *fa,
00323                          double *fb, double *fc,
00324                          rutz::shared_ptr<ShapeModel>& shape)
00325 {
00326   double ulim,u,r,q,fu,dum;
00327 
00328   *fa=f1dim(*ax, shape);
00329   *fb=f1dim(*bx, shape);
00330   if (*fb > *fa) {
00331     SHFT(dum,*ax,*bx,dum)
00332       SHFT(dum,*fb,*fa,dum)
00333       }
00334   *cx=(*bx)+GOLD*(*bx-*ax);
00335   *fc=f1dim(*cx, shape);
00336   while (*fb > *fc) {
00337     r=(*bx-*ax)*(*fb-*fc);
00338     q=(*bx-*cx)*(*fb-*fa);
00339     u=(*bx)-((*bx-*cx)*q-(*bx-*ax)*r)/
00340       (2.0*SIGN(std::max(fabs(q-r),TINY),q-r));
00341     ulim=(*bx)+GLIMIT*(*cx-*bx);
00342     if ((*bx-u)*(u-*cx) > 0.0) {
00343       fu=f1dim(u, shape);
00344       if (fu < *fc) {
00345         *ax=(*bx);
00346         *bx=u;
00347         *fa=(*fb);
00348         *fb=fu;
00349         return;
00350       } else if (fu > *fb) {
00351         *cx=u;
00352         *fc=fu;
00353         return;
00354       }
00355       u=(*cx)+GOLD*(*cx-*bx);
00356       fu=f1dim(u, shape);
00357     } else if ((*cx-u)*(u-ulim) > 0.0) {
00358       fu=f1dim(u, shape);
00359       if (fu < *fc) {
00360         SHFT(*bx,*cx,u,*cx+GOLD*(*cx-*bx))
00361           SHFT(*fb,*fc,fu,f1dim(u, shape))
00362           }
00363     } else if ((u-ulim)*(ulim-*cx) >= 0.0) {
00364       u=ulim;
00365       fu=f1dim(u, shape);
00366     } else {
00367       u=(*cx)+GOLD*(*cx-*bx);
00368       fu=f1dim(u, shape);
00369     }
00370     SHFT(*ax,*bx,*cx,u)
00371       SHFT(*fa,*fb,*fc,fu)
00372       }
00373 }
00374 
00375 double BeoSubCanny::brent(double ax, double bx, double cx,
00376                           double tol, double *xmin,
00377                           rutz::shared_ptr<ShapeModel>& shape)
00378 {
00379         int iter;
00380         double a,b,d=0.0,etemp,fu,fv,fw,fx,p,q,r,tol1,tol2,u,v,w,x,xm;
00381         double e=0.0;
00382 
00383         a=(ax < cx ? ax : cx);
00384         b=(ax > cx ? ax : cx);
00385         x=w=v=bx;
00386         fw=fv=fx=f1dim(x, shape);
00387         for (iter=1;iter<=ITMAXB;iter++) {
00388                 xm=0.5*(a+b);
00389                 tol2=2.0*(tol1=tol*fabs(x)+ZEPS);
00390                 if (fabs(x-xm) <= (tol2-0.5*(b-a))) {
00391                         *xmin=x;
00392                         return fx;
00393                 }
00394                 if (fabs(e) > tol1) {
00395                         r=(x-w)*(fx-fv);
00396                         q=(x-v)*(fx-fw);
00397                         p=(x-v)*q-(x-w)*r;
00398                         q=2.0*(q-r);
00399                         if (q > 0.0) p = -p;
00400                         q=fabs(q);
00401                         etemp=e;
00402                         e=d;
00403                         if (fabs(p) >= fabs(0.5*q*etemp) || p <= q*(a-x) || p >= q*(b-x))
00404                                 d=CGOLD*(e=(x >= xm ? a-x : b-x));
00405                         else {
00406                                 d=p/q;
00407                                 u=x+d;
00408                                 if (u-a < tol2 || b-u < tol2)
00409                                         d=SIGN(tol1,xm-x);
00410                         }
00411                 } else {
00412                         d=CGOLD*(e=(x >= xm ? a-x : b-x));
00413                 }
00414                 u=(fabs(d) >= tol1 ? x+d : x+SIGN(tol1,d));
00415                 fu=f1dim(u, shape);
00416                 if (fu <= fx) {
00417                         if (u >= x) a=x; else b=x;
00418                         SHFT(v,w,x,u)
00419                         SHFT(fv,fw,fx,fu)
00420                 } else {
00421                         if (u < x) a=u; else b=u;
00422                         if (fu <= fw || w == x) {
00423                                 v=w;
00424                                 w=u;
00425                                 fv=fw;
00426                                 fw=fu;
00427                         } else if (fu <= fv || v == x || v == w) {
00428                                 v=u;
00429                                 fv=fu;
00430                         }
00431                 }
00432         }
00433         printf("Too many iterations in brent");
00434         *xmin=x;
00435         return fx;
00436 }
00437 
00438 double BeoSubCanny::f1dim(double x, rutz::shared_ptr<ShapeModel>& shape)
00439 {
00440         int j;
00441         double f, *xt;
00442         xt=nrVector(1,ncom);
00443 
00444         for (j=1;j<=ncom;j++) xt[j]=pcom[j]+x*xicom[j];
00445         f=shape->calcDist(xt, distMap);
00446         free_nrVector(xt,1,ncom);
00447         return f;
00448 }
00449 
00450 void BeoSubCanny::linmin(double p[], double xi[], int n, double *fret,
00451             rutz::shared_ptr<ShapeModel>& optimizee)
00452 {
00453 
00454         int j;
00455         double xx,xmin,fx,fb,fa,bx,ax;
00456 
00457         ncom=n;
00458         pcom=nrVector(1,n);
00459         xicom=nrVector(1,n);
00460         for (j=1;j<=n;j++) {
00461                 pcom[j]=p[j];
00462                 xicom[j]=xi[j];
00463         }
00464 
00465         ax=0.0;
00466         xx=1.0;
00467 
00468           mnbrak(&ax,&xx,&bx,&fa,&fx,&fb,optimizee);
00469           *fret=brent(ax,xx,bx,TOL,&xmin, optimizee);
00470 
00471         for (j=1;j<=n;j++) {
00472                 xi[j] *= xmin;
00473                 p[j] += xi[j];
00474         }
00475         free_nrVector(xicom,1,n);
00476         free_nrVector(pcom,1,n);
00477 }
00478 
00479 bool BeoSubCanny::powell(double p[], double **xi, int n, double ftol,
00480             int *iter, double *fret, rutz::shared_ptr<ShapeModel>& optimizee)
00481 {
00482         int i,ibig,j;
00483         double del,fp,fptt,t, *pt, *ptt, *xit;
00484 
00485         pt=nrVector(1,n);
00486         ptt=nrVector(1,n);
00487         xit=nrVector(1,n);
00488 
00489         fptt = optimizee->getThreshold(); //hacky, to prevent warning
00490 
00491         *fret=optimizee->calcDist(p, distMap);
00492         for (j=1;j<=n;j++) pt[j]=p[j];
00493         for (*iter=1;;++(*iter)) {
00494                 fp=(*fret);
00495                 ibig=0;
00496                 del=0.0;
00497                 for (i=1;i<=n;i++) {
00498                         for (j = 1; j <= n; j++){
00499                           //NOTE: This stupid change using -1 for the xi vector is necessary to handle weirdness of the nrVectors
00500                           xit[j] = xi[j-1][i-1];
00501                         }
00502                         fptt=(*fret);
00503                         linmin(p,xit,n,fret,optimizee);
00504                         if (fabs(fptt-(*fret)) > del) {
00505                                 del=fabs(fptt-(*fret));
00506                                 ibig=i;
00507                         }
00508                 }
00509                 if (2.0*fabs(fp-(*fret)) <= ftol*(fabs(fp)+fabs(*fret))) {
00510 
00511                   //optimizee->setDimensions(p);//TEST
00512 
00513                   //If cost is below threshold, then return that the shape was found
00514                   //perhaps this is not in the most ideal spot. --Z
00515                   if(fptt < optimizee->getThreshold()){//if cost estimate is below threshold, shape is present in image (very rudimentary, need to take dimensions into account)
00516                           return(true);//test
00517                         }
00518                         //otherwise, return that it was not
00519                         else{return(false);}//test
00520                 }
00521                 if (*iter == ITMAX){
00522                   //optimizee->setDimensions(p);//TEST
00523                   printf("powell exceeding maximum iterations.");
00524                   return(false);//test
00525                 }
00526                 for (j=1;j<=n;j++) {
00527                         ptt[j]=2.0*p[j]-pt[j];
00528                         xit[j]=p[j]-pt[j];
00529                         pt[j]=p[j];
00530                 }
00531                 //NOTE: in order to fix problem of things becoming too small or going outside of the image, either put a check right here or check inside calc dist. Then, if values cause trouble, REVERT to values of things in above for loop that were set BEFORE the previous change. This may not help, however, depending on how calcDist is called in its first instance in this function
00532                 //NOTE: it may be better instead to have the calcDist function return values by reference. In that case, a shape could correct its own dimensions if they went out of whack. Unfortunately, this would not work if poweel would still go down the wrong path. FIX!
00533                 fptt=optimizee->calcDist(ptt, distMap);
00534                 if (fptt < fp) {
00535                   t=2.0*(fp-2.0*(*fret)+fptt)*squareOf(fp-(*fret)-del)-del*squareOf(fp-fptt);
00536                   if (t < 0.0) {
00537                     linmin(p,xit,n,fret,optimizee);
00538                     for (j=0;j<n;j++) {
00539                       xi[j][ibig]=xi[j][n];
00540                       xi[j][n]=xit[j];
00541                     }
00542                   }
00543                 }
00544 
00545         }
00546 }
00547 
00548 /*******************************************************************************
00549 * PROCEDURE: canny
00550 * PURPOSE: To perform canny edge detection.
00551 * NAME: Mike Heath
00552 * DATE: 2/15/96
00553 //Pradeep: returns the centroid of the "white" pixels
00554 *******************************************************************************/
00555 int BeoSubCanny::canny(unsigned char *image, int rows, int cols, float sigma,
00556          float tlow, float thigh, unsigned char **edge, char *fname)
00557 {
00558    FILE *fpdir=NULL;          /* File to write the gradient image to.     */
00559    unsigned char *nms;        /* Points that are local maximal magnitude. */
00560    short int *smoothedim,     /* The image after gaussian smoothing.      */
00561              *delta_x,        /* The first devivative image, x-direction. */
00562              *delta_y,        /* The first derivative image, y-direction. */
00563              *magnitude;      /* The magnitude of the gadient image.      */
00564    float *dir_radians=NULL;   /* Gradient direction image.                */
00565 
00566    /****************************************************************************
00567    * Perform gaussian smoothing on the image using the input standard
00568    * deviation.
00569    ****************************************************************************/
00570    gaussian_smooth(image, rows, cols, sigma, &smoothedim);
00571 
00572    /****************************************************************************
00573    * Compute the first derivative in the x and y directions.
00574    ****************************************************************************/
00575    derrivative_x_y(smoothedim, rows, cols, &delta_x, &delta_y);
00576 
00577    /****************************************************************************
00578    * This option to write out the direction of the edge gradient was added
00579    * to make the information available for computing an edge quality figure
00580    * of merit.
00581    ****************************************************************************/
00582    if(fname != NULL){
00583       /*************************************************************************
00584       * Compute the direction up the gradient, in radians that are
00585       * specified counteclockwise from the positive x-axis.
00586       *************************************************************************/
00587       radian_direction(delta_x, delta_y, rows, cols, &dir_radians, -1, -1);
00588 
00589       /*************************************************************************
00590       * Write the gradient direction image out to a file.
00591       *************************************************************************/
00592       if((fpdir = fopen(fname, "wb")) == NULL){
00593          fprintf(stderr, "Error opening the file %s for writing.\n", fname);
00594          exit(1);
00595       }
00596       fwrite(dir_radians, sizeof(float), rows*cols, fpdir);
00597       fclose(fpdir);
00598       free(dir_radians);
00599    }
00600 
00601    /****************************************************************************
00602    * Compute the magnitude of the gradient.
00603    ****************************************************************************/
00604    magnitude_x_y(delta_x, delta_y, rows, cols, &magnitude);
00605 
00606    /****************************************************************************
00607    * Perform non-maximal suppression.
00608    ****************************************************************************/
00609    if((nms = (unsigned char *) calloc(rows*cols,sizeof(unsigned char)))==NULL){
00610       fprintf(stderr, "Error allocating the nms image.\n");
00611       exit(1);
00612    }
00613 
00614    non_max_supp(magnitude, delta_x, delta_y, rows, cols, nms);
00615 
00616    /****************************************************************************
00617    * Use hysteresis to mark the edge pixels.
00618    ****************************************************************************/
00619    if((*edge=(unsigned char *)calloc(rows*cols,sizeof(unsigned char))) ==NULL){
00620       fprintf(stderr, "Error allocating the edge image.\n");
00621       exit(1);
00622    }
00623    //printf("\n%d %d %d %d %d %d\n", magnitude, nms, rows, cols, tlow, thigh);
00624      int centroid = apply_hysteresis(magnitude, nms, rows, cols, tlow, thigh, *edge);
00625    /****************************************************************************
00626    * Free all of the memory that we allocated except for the edge image that
00627    * is still being used to store out result.
00628    ****************************************************************************/
00629    free(smoothedim);
00630    free(delta_x);
00631    free(delta_y);
00632    free(magnitude);
00633    free(nms);
00634    return centroid;
00635 }
00636 
00637 /*******************************************************************************
00638 * Procedure: radian_direction
00639 * Purpose: To compute a direction of the gradient image from component dx and
00640 * dy images. Because not all derriviatives are computed in the same way, this
00641 * code allows for dx or dy to have been calculated in different ways.
00642 *
00643 * FOR X:  xdirtag = -1  for  [-1 0  1]
00644 *         xdirtag =  1  for  [ 1 0 -1]
00645 *
00646 * FOR Y:  ydirtag = -1  for  [-1 0  1]'
00647 *         ydirtag =  1  for  [ 1 0 -1]'
00648 *
00649 * The resulting angle is in radians measured counterclockwise from the
00650 * xdirection. The angle points "up the gradient".
00651 *******************************************************************************/
00652 void BeoSubCanny::radian_direction(short int *delta_x, short int *delta_y, int rows,
00653     int cols, float **dir_radians, int xdirtag, int ydirtag)
00654 {
00655    int r, c, pos;
00656    float *dirim=NULL;
00657    double dx, dy;
00658 
00659    /****************************************************************************
00660    * Allocate an image to store the direction of the gradient.
00661    ****************************************************************************/
00662    if((dirim = (float *) calloc(rows*cols, sizeof(float))) == NULL){
00663       fprintf(stderr, "Error allocating the gradient direction image.\n");
00664       exit(1);
00665    }
00666    *dir_radians = dirim;
00667 
00668    for(r=0,pos=0;r<rows;r++){
00669       for(c=0;c<cols;c++,pos++){
00670          dx = (double)delta_x[pos];
00671          dy = (double)delta_y[pos];
00672 
00673          if(xdirtag == 1) dx = -dx;
00674          if(ydirtag == -1) dy = -dy;
00675 
00676          dirim[pos] = (float)angle_radians(dx, dy);
00677       }
00678    }
00679 }
00680 
00681 /*******************************************************************************
00682 * FUNCTION: angle_radians
00683 * PURPOSE: This procedure computes the angle of a vector with components x and
00684 * y. It returns this angle in radians with the answer being in the range
00685 * 0 <= angle <2*PI.
00686 *******************************************************************************/
00687 double BeoSubCanny::angle_radians(double x, double y)
00688 {
00689    double xu, yu, ang;
00690 
00691    xu = fabs(x);
00692    yu = fabs(y);
00693 
00694    if((xu == 0) && (yu == 0)) return(0);
00695 
00696    ang = atan(yu/xu);
00697 
00698    if(x >= 0){
00699       if(y >= 0) return(ang);
00700       else return(2*M_PI - ang);
00701    }
00702    else{
00703       if(y >= 0) return(M_PI - ang);
00704       else return(M_PI + ang);
00705    }
00706 }
00707 
00708 /*******************************************************************************
00709 * PROCEDURE: magnitude_x_y
00710 * PURPOSE: Compute the magnitude of the gradient. This is the square root of
00711 * the sum of the squared derivative values.
00712 * NAME: Mike Heath
00713 * DATE: 2/15/96
00714 *******************************************************************************/
00715 void BeoSubCanny::magnitude_x_y(short int *delta_x, short int *delta_y, int rows, int cols,
00716         short int **magnitude)
00717 {
00718    int r, c, pos, sq1, sq2;
00719 
00720    /****************************************************************************
00721    * Allocate an image to store the magnitude of the gradient.
00722    ****************************************************************************/
00723    if((*magnitude = (short *) calloc(rows*cols, sizeof(short))) == NULL){
00724       fprintf(stderr, "Error allocating the magnitude image.\n");
00725       exit(1);
00726    }
00727 
00728    for(r=0,pos=0;r<rows;r++){
00729       for(c=0;c<cols;c++,pos++){
00730          sq1 = (int)delta_x[pos] * (int)delta_x[pos];
00731          sq2 = (int)delta_y[pos] * (int)delta_y[pos];
00732          (*magnitude)[pos] = (short)(0.5 + sqrt((float)sq1 + (float)sq2));
00733       }
00734    }
00735 
00736 }
00737 
00738 /*******************************************************************************
00739 * PROCEDURE: derrivative_x_y
00740 * PURPOSE: Compute the first derivative of the image in both the x any y
00741 * directions. The differential filters that are used are:
00742 *
00743 *                                          -1
00744 *         dx =  -1 0 +1     and       dy =  0
00745 *                                          +1
00746 *
00747 * NAME: Mike Heath
00748 * DATE: 2/15/96
00749 *******************************************************************************/
00750 void BeoSubCanny::derrivative_x_y(short int *smoothedim, int rows, int cols,
00751         short int **delta_x, short int **delta_y)
00752 {
00753    int r, c, pos;
00754 
00755    /****************************************************************************
00756    * Allocate images to store the derivatives.
00757    ****************************************************************************/
00758    if(((*delta_x) = (short *) calloc(rows*cols, sizeof(short))) == NULL){
00759       fprintf(stderr, "Error allocating the delta_x image.\n");
00760       exit(1);
00761    }
00762    if(((*delta_y) = (short *) calloc(rows*cols, sizeof(short))) == NULL){
00763       fprintf(stderr, "Error allocating the delta_x image.\n");
00764       exit(1);
00765    }
00766 
00767    /****************************************************************************
00768    * Compute the x-derivative. Adjust the derivative at the borders to avoid
00769    * losing pixels.
00770    ****************************************************************************/
00771    for(r=0;r<rows;r++){
00772       pos = r * cols;
00773       (*delta_x)[pos] = smoothedim[pos+1] - smoothedim[pos];
00774       pos++;
00775       for(c=1;c<(cols-1);c++,pos++){
00776          (*delta_x)[pos] = smoothedim[pos+1] - smoothedim[pos-1];
00777       }
00778       (*delta_x)[pos] = smoothedim[pos] - smoothedim[pos-1];
00779    }
00780 
00781    /****************************************************************************
00782    * Compute the y-derivative. Adjust the derivative at the borders to avoid
00783    * losing pixels.
00784    ****************************************************************************/
00785    for(c=0;c<cols;c++){
00786       pos = c;
00787       (*delta_y)[pos] = smoothedim[pos+cols] - smoothedim[pos];
00788       pos += cols;
00789       for(r=1;r<(rows-1);r++,pos+=cols){
00790          (*delta_y)[pos] = smoothedim[pos+cols] - smoothedim[pos-cols];
00791       }
00792       (*delta_y)[pos] = smoothedim[pos] - smoothedim[pos-cols];
00793    }
00794 }
00795 
00796 /*******************************************************************************
00797 * PROCEDURE: gaussian_smooth
00798 * PURPOSE: Blur an image with a gaussian filter.
00799 * NAME: Mike Heath
00800 * DATE: 2/15/96
00801 *******************************************************************************/
00802 void BeoSubCanny::gaussian_smooth(unsigned char *image, int rows, int cols, float sigma,
00803         short int **smoothedim)
00804 {
00805    int r, c, rr, cc,     /* Counter variables. */
00806       windowsize,        /* Dimension of the gaussian kernel. */
00807       center;            /* Half of the windowsize. */
00808    float *tempim,        /* Buffer for separable filter gaussian smoothing. */
00809          *kernel,        /* A one dimensional gaussian kernel. */
00810          dot,            /* Dot product summing variable. */
00811          sum;            /* Sum of the kernel weights variable. */
00812 
00813    /****************************************************************************
00814    * Create a 1-dimensional gaussian smoothing kernel.
00815    ****************************************************************************/
00816    make_gaussian_kernel(sigma, &kernel, &windowsize);
00817    center = windowsize / 2;
00818 
00819    /****************************************************************************
00820    * Allocate a temporary buffer image and the smoothed image.
00821    ****************************************************************************/
00822    if((tempim = (float *) calloc(rows*cols, sizeof(float))) == NULL){
00823       fprintf(stderr, "Error allocating the buffer image.\n");
00824       exit(1);
00825    }
00826    if(((*smoothedim) = (short int *) calloc(rows*cols,
00827          sizeof(short int))) == NULL){
00828       fprintf(stderr, "Error allocating the smoothed image.\n");
00829       exit(1);
00830    }
00831 
00832    /****************************************************************************
00833    * Blur in the x - direction.
00834    ****************************************************************************/
00835    for(r=0;r<rows;r++){
00836       for(c=0;c<cols;c++){
00837          dot = 0.0;
00838          sum = 0.0;
00839          for(cc=(-center);cc<=center;cc++){
00840             if(((c+cc) >= 0) && ((c+cc) < cols)){
00841                dot += (float)image[r*cols+(c+cc)] * kernel[center+cc];
00842                sum += kernel[center+cc];
00843             }
00844          }
00845          tempim[r*cols+c] = dot/sum;
00846       }
00847    }
00848 
00849    /****************************************************************************
00850    * Blur in the y - direction.
00851    ****************************************************************************/
00852    for(c=0;c<cols;c++){
00853       for(r=0;r<rows;r++){
00854          sum = 0.0;
00855          dot = 0.0;
00856          for(rr=(-center);rr<=center;rr++){
00857             if(((r+rr) >= 0) && ((r+rr) < rows)){
00858                dot += tempim[(r+rr)*cols+c] * kernel[center+rr];
00859                sum += kernel[center+rr];
00860             }
00861          }
00862          (*smoothedim)[r*cols+c] = (short int)(dot*BOOSTBLURFACTOR/sum + 0.5);
00863       }
00864    }
00865 
00866    free(tempim);
00867    free(kernel);
00868 }
00869 
00870 /*******************************************************************************
00871 * PROCEDURE: make_gaussian_kernel
00872 * PURPOSE: Create a one dimensional gaussian kernel.
00873 * NAME: Mike Heath
00874 * DATE: 2/15/96
00875 *******************************************************************************/
00876 void BeoSubCanny::make_gaussian_kernel(float sigma, float **kernel, int *windowsize)
00877 {
00878    int i, center;
00879    float x, fx, sum=0.0;
00880 
00881    *windowsize = int(1 + 2 * ceil(2.5 * sigma));
00882    center = (*windowsize) / 2;
00883 
00884    if((*kernel = (float *) calloc((*windowsize), sizeof(float))) == NULL){
00885       fprintf(stderr, "Error callocing the gaussian kernel array.\n");
00886       exit(1);
00887    }
00888 
00889    for(i=0;i<(*windowsize);i++){
00890       x = (float)(i - center);
00891       fx = pow(2.71828, -0.5*x*x/(sigma*sigma)) / (sigma * sqrt(6.2831853));
00892       (*kernel)[i] = fx;
00893       sum += fx;
00894    }
00895 
00896    for(i=0;i<(*windowsize);i++) (*kernel)[i] /= sum;
00897 
00898 }
Generated on Sun May 8 08:04:32 2011 for iLab Neuromorphic Vision Toolkit by  doxygen 1.6.3