BeoSubShapeDetector.C

Go to the documentation of this file.
00001 /*!@file BeoSub/BeoSubShapeDetector.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/BeoSubShapeDetector.C $
00035 // $Id: BeoSubShapeDetector.C 9412 2008-03-10 23:10:15Z farhan $
00036 //
00037 
00038 #include "BeoSub/BeoSubShapeDetector.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 color[0] = 0.79F; color[1] = 0.18F; color[2] = 0.22F; color[3] = 0.175F;
00125   }
00126   else{
00127     useColor = false;
00128   }
00129 
00130   //! +/- tollerance value on mean for track
00131   std::vector<float> std(4,0.0F);
00132   //NOTE that the saturation tolerance is important (if it goes any higher than this, it will nearly always recognize white!)
00133   std[0] = 0.30000; std[1] = 0.30000; std[2] = 0.34500; std[3] = 0.15000;//3 was .45, 2 was .445
00134 
00135   if(!strcmp(colorArg, "White") || !strcmp(colorArg, "Black")){
00136     std[0] = 1.0F; std[1] = 1.0F; std[2] = 0.1F; std[3] = 0.1F;
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 
00237  canny(grayImg.getArrayPtr(), grayImg.getHeight(), grayImg.getWidth(), sigma, tlow, thigh, &edge, NULL);
00238 
00239   Image<unsigned char> cannyCharImg(edge, grayImg.getWidth(), grayImg.getHeight());
00240 
00241   Image<float> cannyImg = cannyCharImg;
00242 
00243   Point2D<int> cent = centroid(cannyCharImg);
00244   distMap = chamfer34(cannyImg, 5000.0f);
00245 
00246  distMap = grayImg;
00247 
00248   //Raster::WriteGray(cannyImg, outfilename, RASFMT_PNM);//edge image
00249   if(debugmode){
00250     if(!xwin.get()){
00251       xwin.reset(new XWindow(colorImg.getDims()));
00252       xwin->setPosition(colorImg.getWidth()+10, 0);
00253     }
00254     xwin->drawImage((Image< PixRGB<byte> >)grayImg);
00255   }
00256 
00257 
00258 
00259   //perform shape detection
00260   //get initial dimensions for the shape
00261 
00262   bool shapeFound = false;
00263   double** xi = (double**)calloc(6, sizeof(double));
00264 
00265 
00266    //NOTE: HERE is where the initial direction matrix is built. Change these values to change axis assignment
00267    for(int i=0; i<6; i++) {
00268      xi[i] = (double*)calloc(6, sizeof(double));
00269      xi[i][i] = 1.0;
00270    }
00271    //Reset initial axes
00272    xi[0][0] = 0.5;
00273    xi[0][1] = 0.5;
00274 
00275   int iter = 0;
00276   double fret = 0.0;
00277 
00278   //here, we will be using powell classes, and, instead of passing in calcDist, will be passing in a shape.
00279    shapeFound = powell(shapeArg->getDimensions(), xi, shapeArg->getNumDims(), 0.5, &iter, &fret, shapeArg);
00280 
00281    if(debugmode){
00282      double* p = shapeArg->getDimensions();//TEST
00283      if(shapeFound){
00284        printf("\n\nShape found!\n");
00285        printf("Final dimensions are -- ");
00286        for(int w = 1; w <=shapeArg->getNumDims(); w++){
00287          printf("%d: %f ", w, p[w]);//outputs final values
00288        }
00289        printf("Certainty (lower is better): %f\n\n", fret);
00290      }else{
00291        printf("\n\nShape not found. Certainty was %f\n\n", fret);
00292      }
00293      }
00294 
00295    return(shapeFound);
00296 }
00297 
00298 
00299 
00300 // ######################################################################
00301 BeoSubCanny::~BeoSubCanny()
00302 {
00303 }
00304 
00305 ///////////////////////FROM NRUTIL/////////////////////////
00306 // ######################################################################
00307 double *BeoSubCanny::nrVector(long nl, long nh)
00308 /* allocate a double vector with subscript range v[nl..nh] */
00309 {
00310         double *v;
00311 
00312         v=(double *)malloc((size_t) ((nh-nl+2)*sizeof(double)));
00313         if (!v) printf("allocation failure in nrVector()");
00314         return v-nl+1;
00315 }
00316 
00317 // ######################################################################
00318 void BeoSubCanny::free_nrVector(double *v, long nl, long nh)
00319 /* free a double vector allocated with nrVector() */
00320 {
00321         free((FREE_ARG) (v+nl-1));
00322 }
00323 
00324 
00325 // ######################################################################
00326 void BeoSubCanny::mnbrak(double *ax, double *bx, double *cx, double *fa,
00327                          double *fb, double *fc,
00328                          rutz::shared_ptr<ShapeModel>& shape)
00329 {
00330   double ulim,u,r,q,fu,dum;
00331 
00332   *fa=f1dim(*ax, shape);
00333   *fb=f1dim(*bx, shape);
00334   if (*fb > *fa) {
00335     SHFT(dum,*ax,*bx,dum)
00336       SHFT(dum,*fb,*fa,dum)
00337       }
00338   *cx=(*bx)+GOLD*(*bx-*ax);
00339   *fc=f1dim(*cx, shape);
00340   while (*fb > *fc) {
00341     r=(*bx-*ax)*(*fb-*fc);
00342     q=(*bx-*cx)*(*fb-*fa);
00343     u=(*bx)-((*bx-*cx)*q-(*bx-*ax)*r)/
00344       (2.0*SIGN(std::max(fabs(q-r),TINY),q-r));
00345     ulim=(*bx)+GLIMIT*(*cx-*bx);
00346     if ((*bx-u)*(u-*cx) > 0.0) {
00347       fu=f1dim(u, shape);
00348       if (fu < *fc) {
00349         *ax=(*bx);
00350         *bx=u;
00351         *fa=(*fb);
00352         *fb=fu;
00353         return;
00354       } else if (fu > *fb) {
00355         *cx=u;
00356         *fc=fu;
00357         return;
00358       }
00359       u=(*cx)+GOLD*(*cx-*bx);
00360       fu=f1dim(u, shape);
00361     } else if ((*cx-u)*(u-ulim) > 0.0) {
00362       fu=f1dim(u, shape);
00363       if (fu < *fc) {
00364         SHFT(*bx,*cx,u,*cx+GOLD*(*cx-*bx))
00365           SHFT(*fb,*fc,fu,f1dim(u, shape))
00366           }
00367     } else if ((u-ulim)*(ulim-*cx) >= 0.0) {
00368       u=ulim;
00369       fu=f1dim(u, shape);
00370     } else {
00371       u=(*cx)+GOLD*(*cx-*bx);
00372       fu=f1dim(u, shape);
00373     }
00374     SHFT(*ax,*bx,*cx,u)
00375       SHFT(*fa,*fb,*fc,fu)
00376       }
00377 }
00378 
00379 double BeoSubCanny::brent(double ax, double bx, double cx,
00380                           double tol, double *xmin,
00381                           rutz::shared_ptr<ShapeModel>& shape)
00382 {
00383         int iter;
00384         double a,b,d=0.0,etemp,fu,fv,fw,fx,p,q,r,tol1,tol2,u,v,w,x,xm;
00385         double e=0.0;
00386 
00387         a=(ax < cx ? ax : cx);
00388         b=(ax > cx ? ax : cx);
00389         x=w=v=bx;
00390         fw=fv=fx=f1dim(x, shape);
00391         for (iter=1;iter<=ITMAXB;iter++) {
00392                 xm=0.5*(a+b);
00393                 tol2=2.0*(tol1=tol*fabs(x)+ZEPS);
00394                 if (fabs(x-xm) <= (tol2-0.5*(b-a))) {
00395                         *xmin=x;
00396                         return fx;
00397                 }
00398                 if (fabs(e) > tol1) {
00399                         r=(x-w)*(fx-fv);
00400                         q=(x-v)*(fx-fw);
00401                         p=(x-v)*q-(x-w)*r;
00402                         q=2.0*(q-r);
00403                         if (q > 0.0) p = -p;
00404                         q=fabs(q);
00405                         etemp=e;
00406                         e=d;
00407                         if (fabs(p) >= fabs(0.5*q*etemp) || p <= q*(a-x) || p >= q*(b-x))
00408                                 d=CGOLD*(e=(x >= xm ? a-x : b-x));
00409                         else {
00410                                 d=p/q;
00411                                 u=x+d;
00412                                 if (u-a < tol2 || b-u < tol2)
00413                                         d=SIGN(tol1,xm-x);
00414                         }
00415                 } else {
00416                         d=CGOLD*(e=(x >= xm ? a-x : b-x));
00417                 }
00418                 u=(fabs(d) >= tol1 ? x+d : x+SIGN(tol1,d));
00419                 fu=f1dim(u, shape);
00420                 if (fu <= fx) {
00421                         if (u >= x) a=x; else b=x;
00422                         SHFT(v,w,x,u)
00423                         SHFT(fv,fw,fx,fu)
00424                 } else {
00425                         if (u < x) a=u; else b=u;
00426                         if (fu <= fw || w == x) {
00427                                 v=w;
00428                                 w=u;
00429                                 fv=fw;
00430                                 fw=fu;
00431                         } else if (fu <= fv || v == x || v == w) {
00432                                 v=u;
00433                                 fv=fu;
00434                         }
00435                 }
00436         }
00437         printf("Too many iterations in brent");
00438         *xmin=x;
00439         return fx;
00440 }
00441 
00442 double BeoSubCanny::f1dim(double x, rutz::shared_ptr<ShapeModel>& shape)
00443 {
00444         int j;
00445         double f, *xt;
00446         xt=nrVector(1,ncom);
00447 
00448         for (j=1;j<=ncom;j++) xt[j]=pcom[j]+x*xicom[j];
00449         f=shape->calcDist(xt, distMap);
00450         free_nrVector(xt,1,ncom);
00451         return f;
00452 }
00453 
00454 void BeoSubCanny::linmin(double p[], double xi[], int n, double *fret,
00455             rutz::shared_ptr<ShapeModel>& optimizee)
00456 {
00457 
00458         int j;
00459         double xx,xmin,fx,fb,fa,bx,ax;
00460 
00461         ncom=n;
00462         pcom=nrVector(1,n);
00463         xicom=nrVector(1,n);
00464         for (j=1;j<=n;j++) {
00465                 pcom[j]=p[j];
00466                 xicom[j]=xi[j];
00467         }
00468 
00469         ax=0.0;
00470         xx=1.0;
00471 
00472           mnbrak(&ax,&xx,&bx,&fa,&fx,&fb,optimizee);
00473           *fret=brent(ax,xx,bx,TOL,&xmin, optimizee);
00474 
00475         for (j=1;j<=n;j++) {
00476                 xi[j] *= xmin;
00477                 p[j] += xi[j];
00478         }
00479         free_nrVector(xicom,1,n);
00480         free_nrVector(pcom,1,n);
00481 }
00482 
00483 bool BeoSubCanny::powell(double p[], double **xi, int n, double ftol,
00484             int *iter, double *fret, rutz::shared_ptr<ShapeModel>& optimizee)
00485 {
00486         int i,ibig,j;
00487         double del,fp,fptt,t, *pt, *ptt, *xit;
00488 
00489         pt=nrVector(1,n);
00490         ptt=nrVector(1,n);
00491         xit=nrVector(1,n);
00492 
00493         fptt = optimizee->getThreshold(); //hacky, to prevent warning
00494 
00495         *fret=optimizee->calcDist(p, distMap);
00496         for (j=1;j<=n;j++) pt[j]=p[j];
00497         for (*iter=1;;++(*iter)) {
00498                 fp=(*fret);
00499                 ibig=0;
00500                 del=0.0;
00501                 for (i=1;i<=n;i++) {
00502                         for (j = 1; j <= n; j++){
00503                           //NOTE: This stupid change using -1 for the xi vector is necessary to handle weirdness of the nrVectors
00504                           xit[j] = xi[j-1][i-1];
00505                         }
00506                         fptt=(*fret);
00507                         linmin(p,xit,n,fret,optimizee);
00508                         if (fabs(fptt-(*fret)) > del) {
00509                                 del=fabs(fptt-(*fret));
00510                                 ibig=i;
00511                         }
00512                 }
00513                 if (2.0*fabs(fp-(*fret)) <= ftol*(fabs(fp)+fabs(*fret))) {
00514 
00515                   //optimizee->setDimensions(p);//TEST
00516 
00517                   //If cost is below threshold, then return that the shape was found
00518                   //perhaps this is not in the most ideal spot. --Z
00519                   if(fptt < optimizee->getThreshold()){//if cost estimate is below threshold, shape is present in image (very rudimentary, need to take dimensions into account)
00520                           return(true);//test
00521                         }
00522                         //otherwise, return that it was not
00523                         else{return(false);}//test
00524                 }
00525                 if (*iter == ITMAX){
00526                   //optimizee->setDimensions(p);//TEST
00527                   printf("powell exceeding maximum iterations.");
00528                   return(false);//test
00529                 }
00530                 for (j=1;j<=n;j++) {
00531                         ptt[j]=2.0*p[j]-pt[j];
00532                         xit[j]=p[j]-pt[j];
00533                         pt[j]=p[j];
00534                 }
00535                 //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
00536                 //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!
00537                 fptt=optimizee->calcDist(ptt, distMap);
00538                 if (fptt < fp) {
00539                   t=2.0*(fp-2.0*(*fret)+fptt)*squareOf(fp-(*fret)-del)-del*squareOf(fp-fptt);
00540                   if (t < 0.0) {
00541                     linmin(p,xit,n,fret,optimizee);
00542                     for (j=0;j<n;j++) {
00543                       xi[j][ibig]=xi[j][n];
00544                       xi[j][n]=xit[j];
00545                     }
00546                   }
00547                 }
00548 
00549         }
00550 }
00551 
00552 /*******************************************************************************
00553 * PROCEDURE: canny
00554 * PURPOSE: To perform canny edge detection.
00555 * NAME: Mike Heath
00556 * DATE: 2/15/96
00557 //Pradeep: returns the centroid of the "white" pixels
00558 *******************************************************************************/
00559 int BeoSubCanny::canny(unsigned char *image, int rows, int cols, float sigma,
00560          float tlow, float thigh, unsigned char **edge, char *fname)
00561 {
00562    FILE *fpdir=NULL;          /* File to write the gradient image to.     */
00563    unsigned char *nms;        /* Points that are local maximal magnitude. */
00564    short int *smoothedim,     /* The image after gaussian smoothing.      */
00565              *delta_x,        /* The first devivative image, x-direction. */
00566              *delta_y,        /* The first derivative image, y-direction. */
00567              *magnitude;      /* The magnitude of the gadient image.      */
00568    float *dir_radians=NULL;   /* Gradient direction image.                */
00569 
00570    /****************************************************************************
00571    * Perform gaussian smoothing on the image using the input standard
00572    * deviation.
00573    ****************************************************************************/
00574    gaussian_smooth(image, rows, cols, sigma, &smoothedim);
00575 
00576    /****************************************************************************
00577    * Compute the first derivative in the x and y directions.
00578    ****************************************************************************/
00579    derrivative_x_y(smoothedim, rows, cols, &delta_x, &delta_y);
00580 
00581    /****************************************************************************
00582    * This option to write out the direction of the edge gradient was added
00583    * to make the information available for computing an edge quality figure
00584    * of merit.
00585    ****************************************************************************/
00586    if(fname != NULL){
00587       /*************************************************************************
00588       * Compute the direction up the gradient, in radians that are
00589       * specified counteclockwise from the positive x-axis.
00590       *************************************************************************/
00591       radian_direction(delta_x, delta_y, rows, cols, &dir_radians, -1, -1);
00592 
00593       /*************************************************************************
00594       * Write the gradient direction image out to a file.
00595       *************************************************************************/
00596       if((fpdir = fopen(fname, "wb")) == NULL){
00597          fprintf(stderr, "Error opening the file %s for writing.\n", fname);
00598          exit(1);
00599       }
00600       fwrite(dir_radians, sizeof(float), rows*cols, fpdir);
00601       fclose(fpdir);
00602       free(dir_radians);
00603    }
00604 
00605    /****************************************************************************
00606    * Compute the magnitude of the gradient.
00607    ****************************************************************************/
00608    magnitude_x_y(delta_x, delta_y, rows, cols, &magnitude);
00609 
00610    /****************************************************************************
00611    * Perform non-maximal suppression.
00612    ****************************************************************************/
00613    if((nms = (unsigned char *) calloc(rows*cols,sizeof(unsigned char)))==NULL){
00614       fprintf(stderr, "Error allocating the nms image.\n");
00615       exit(1);
00616    }
00617 
00618    non_max_supp(magnitude, delta_x, delta_y, rows, cols, nms);
00619 
00620    /****************************************************************************
00621    * Use hysteresis to mark the edge pixels.
00622    ****************************************************************************/
00623    if((*edge=(unsigned char *)calloc(rows*cols,sizeof(unsigned char))) ==NULL){
00624       fprintf(stderr, "Error allocating the edge image.\n");
00625       exit(1);
00626    }
00627    //printf("\n%d %d %d %d %d %d\n", magnitude, nms, rows, cols, tlow, thigh);
00628      int centroid = apply_hysteresis(magnitude, nms, rows, cols, tlow, thigh, *edge);
00629    /****************************************************************************
00630    * Free all of the memory that we allocated except for the edge image that
00631    * is still being used to store out result.
00632    ****************************************************************************/
00633    free(smoothedim);
00634    free(delta_x);
00635    free(delta_y);
00636    free(magnitude);
00637    free(nms);
00638    return centroid;
00639 }
00640 
00641 /*******************************************************************************
00642 * Procedure: radian_direction
00643 * Purpose: To compute a direction of the gradient image from component dx and
00644 * dy images. Because not all derriviatives are computed in the same way, this
00645 * code allows for dx or dy to have been calculated in different ways.
00646 *
00647 * FOR X:  xdirtag = -1  for  [-1 0  1]
00648 *         xdirtag =  1  for  [ 1 0 -1]
00649 *
00650 * FOR Y:  ydirtag = -1  for  [-1 0  1]'
00651 *         ydirtag =  1  for  [ 1 0 -1]'
00652 *
00653 * The resulting angle is in radians measured counterclockwise from the
00654 * xdirection. The angle points "up the gradient".
00655 *******************************************************************************/
00656 void BeoSubCanny::radian_direction(short int *delta_x, short int *delta_y, int rows,
00657     int cols, float **dir_radians, int xdirtag, int ydirtag)
00658 {
00659    int r, c, pos;
00660    float *dirim=NULL;
00661    double dx, dy;
00662 
00663    /****************************************************************************
00664    * Allocate an image to store the direction of the gradient.
00665    ****************************************************************************/
00666    if((dirim = (float *) calloc(rows*cols, sizeof(float))) == NULL){
00667       fprintf(stderr, "Error allocating the gradient direction image.\n");
00668       exit(1);
00669    }
00670    *dir_radians = dirim;
00671 
00672    for(r=0,pos=0;r<rows;r++){
00673       for(c=0;c<cols;c++,pos++){
00674          dx = (double)delta_x[pos];
00675          dy = (double)delta_y[pos];
00676 
00677          if(xdirtag == 1) dx = -dx;
00678          if(ydirtag == -1) dy = -dy;
00679 
00680          dirim[pos] = (float)angle_radians(dx, dy);
00681       }
00682    }
00683 }
00684 
00685 /*******************************************************************************
00686 * FUNCTION: angle_radians
00687 * PURPOSE: This procedure computes the angle of a vector with components x and
00688 * y. It returns this angle in radians with the answer being in the range
00689 * 0 <= angle <2*PI.
00690 *******************************************************************************/
00691 double BeoSubCanny::angle_radians(double x, double y)
00692 {
00693    double xu, yu, ang;
00694 
00695    xu = fabs(x);
00696    yu = fabs(y);
00697 
00698    if((xu == 0) && (yu == 0)) return(0);
00699 
00700    ang = atan(yu/xu);
00701 
00702    if(x >= 0){
00703       if(y >= 0) return(ang);
00704       else return(2*M_PI - ang);
00705    }
00706    else{
00707       if(y >= 0) return(M_PI - ang);
00708       else return(M_PI + ang);
00709    }
00710 }
00711 
00712 /*******************************************************************************
00713 * PROCEDURE: magnitude_x_y
00714 * PURPOSE: Compute the magnitude of the gradient. This is the square root of
00715 * the sum of the squared derivative values.
00716 * NAME: Mike Heath
00717 * DATE: 2/15/96
00718 *******************************************************************************/
00719 void BeoSubCanny::magnitude_x_y(short int *delta_x, short int *delta_y, int rows, int cols,
00720         short int **magnitude)
00721 {
00722    int r, c, pos, sq1, sq2;
00723 
00724    /****************************************************************************
00725    * Allocate an image to store the magnitude of the gradient.
00726    ****************************************************************************/
00727    if((*magnitude = (short *) calloc(rows*cols, sizeof(short))) == NULL){
00728       fprintf(stderr, "Error allocating the magnitude image.\n");
00729       exit(1);
00730    }
00731 
00732    for(r=0,pos=0;r<rows;r++){
00733       for(c=0;c<cols;c++,pos++){
00734          sq1 = (int)delta_x[pos] * (int)delta_x[pos];
00735          sq2 = (int)delta_y[pos] * (int)delta_y[pos];
00736          (*magnitude)[pos] = (short)(0.5 + sqrt((float)sq1 + (float)sq2));
00737       }
00738    }
00739 
00740 }
00741 
00742 /*******************************************************************************
00743 * PROCEDURE: derrivative_x_y
00744 * PURPOSE: Compute the first derivative of the image in both the x any y
00745 * directions. The differential filters that are used are:
00746 *
00747 *                                          -1
00748 *         dx =  -1 0 +1     and       dy =  0
00749 *                                          +1
00750 *
00751 * NAME: Mike Heath
00752 * DATE: 2/15/96
00753 *******************************************************************************/
00754 void BeoSubCanny::derrivative_x_y(short int *smoothedim, int rows, int cols,
00755         short int **delta_x, short int **delta_y)
00756 {
00757    int r, c, pos;
00758 
00759    /****************************************************************************
00760    * Allocate images to store the derivatives.
00761    ****************************************************************************/
00762    if(((*delta_x) = (short *) calloc(rows*cols, sizeof(short))) == NULL){
00763       fprintf(stderr, "Error allocating the delta_x image.\n");
00764       exit(1);
00765    }
00766    if(((*delta_y) = (short *) calloc(rows*cols, sizeof(short))) == NULL){
00767       fprintf(stderr, "Error allocating the delta_x image.\n");
00768       exit(1);
00769    }
00770 
00771    /****************************************************************************
00772    * Compute the x-derivative. Adjust the derivative at the borders to avoid
00773    * losing pixels.
00774    ****************************************************************************/
00775    for(r=0;r<rows;r++){
00776       pos = r * cols;
00777       (*delta_x)[pos] = smoothedim[pos+1] - smoothedim[pos];
00778       pos++;
00779       for(c=1;c<(cols-1);c++,pos++){
00780          (*delta_x)[pos] = smoothedim[pos+1] - smoothedim[pos-1];
00781       }
00782       (*delta_x)[pos] = smoothedim[pos] - smoothedim[pos-1];
00783    }
00784 
00785    /****************************************************************************
00786    * Compute the y-derivative. Adjust the derivative at the borders to avoid
00787    * losing pixels.
00788    ****************************************************************************/
00789    for(c=0;c<cols;c++){
00790       pos = c;
00791       (*delta_y)[pos] = smoothedim[pos+cols] - smoothedim[pos];
00792       pos += cols;
00793       for(r=1;r<(rows-1);r++,pos+=cols){
00794          (*delta_y)[pos] = smoothedim[pos+cols] - smoothedim[pos-cols];
00795       }
00796       (*delta_y)[pos] = smoothedim[pos] - smoothedim[pos-cols];
00797    }
00798 }
00799 
00800 /*******************************************************************************
00801 * PROCEDURE: gaussian_smooth
00802 * PURPOSE: Blur an image with a gaussian filter.
00803 * NAME: Mike Heath
00804 * DATE: 2/15/96
00805 *******************************************************************************/
00806 void BeoSubCanny::gaussian_smooth(unsigned char *image, int rows, int cols, float sigma,
00807         short int **smoothedim)
00808 {
00809    int r, c, rr, cc,     /* Counter variables. */
00810       windowsize,        /* Dimension of the gaussian kernel. */
00811       center;            /* Half of the windowsize. */
00812    float *tempim,        /* Buffer for separable filter gaussian smoothing. */
00813          *kernel,        /* A one dimensional gaussian kernel. */
00814          dot,            /* Dot product summing variable. */
00815          sum;            /* Sum of the kernel weights variable. */
00816 
00817    /****************************************************************************
00818    * Create a 1-dimensional gaussian smoothing kernel.
00819    ****************************************************************************/
00820    make_gaussian_kernel(sigma, &kernel, &windowsize);
00821    center = windowsize / 2;
00822 
00823    /****************************************************************************
00824    * Allocate a temporary buffer image and the smoothed image.
00825    ****************************************************************************/
00826    if((tempim = (float *) calloc(rows*cols, sizeof(float))) == NULL){
00827       fprintf(stderr, "Error allocating the buffer image.\n");
00828       exit(1);
00829    }
00830    if(((*smoothedim) = (short int *) calloc(rows*cols,
00831          sizeof(short int))) == NULL){
00832       fprintf(stderr, "Error allocating the smoothed image.\n");
00833       exit(1);
00834    }
00835 
00836    /****************************************************************************
00837    * Blur in the x - direction.
00838    ****************************************************************************/
00839    for(r=0;r<rows;r++){
00840       for(c=0;c<cols;c++){
00841          dot = 0.0;
00842          sum = 0.0;
00843          for(cc=(-center);cc<=center;cc++){
00844             if(((c+cc) >= 0) && ((c+cc) < cols)){
00845                dot += (float)image[r*cols+(c+cc)] * kernel[center+cc];
00846                sum += kernel[center+cc];
00847             }
00848          }
00849          tempim[r*cols+c] = dot/sum;
00850       }
00851    }
00852 
00853    /****************************************************************************
00854    * Blur in the y - direction.
00855    ****************************************************************************/
00856    for(c=0;c<cols;c++){
00857       for(r=0;r<rows;r++){
00858          sum = 0.0;
00859          dot = 0.0;
00860          for(rr=(-center);rr<=center;rr++){
00861             if(((r+rr) >= 0) && ((r+rr) < rows)){
00862                dot += tempim[(r+rr)*cols+c] * kernel[center+rr];
00863                sum += kernel[center+rr];
00864             }
00865          }
00866          (*smoothedim)[r*cols+c] = (short int)(dot*BOOSTBLURFACTOR/sum + 0.5);
00867       }
00868    }
00869 
00870    free(tempim);
00871    free(kernel);
00872 }
00873 
00874 /*******************************************************************************
00875 * PROCEDURE: make_gaussian_kernel
00876 * PURPOSE: Create a one dimensional gaussian kernel.
00877 * NAME: Mike Heath
00878 * DATE: 2/15/96
00879 *******************************************************************************/
00880 void BeoSubCanny::make_gaussian_kernel(float sigma, float **kernel, int *windowsize)
00881 {
00882    int i, center;
00883    float x, fx, sum=0.0;
00884 
00885    *windowsize = int(1 + 2 * ceil(2.5 * sigma));
00886    center = (*windowsize) / 2;
00887 
00888    if((*kernel = (float *) calloc((*windowsize), sizeof(float))) == NULL){
00889       fprintf(stderr, "Error callocing the gaussian kernel array.\n");
00890       exit(1);
00891    }
00892 
00893    for(i=0;i<(*windowsize);i++){
00894       x = (float)(i - center);
00895       fx = pow(2.71828, -0.5*x*x/(sigma*sigma)) / (sigma * sqrt(6.2831853));
00896       (*kernel)[i] = fx;
00897       sum += fx;
00898    }
00899 
00900    for(i=0;i<(*windowsize);i++) (*kernel)[i] /= sum;
00901 
00902 }
Generated on Sun May 8 08:04:32 2011 for iLab Neuromorphic Vision Toolkit by  doxygen 1.6.3