canny.h

Go to the documentation of this file.
00001 
00002 // WARNING! DO NOT USE THIS CLASS
00003 // This class has been replaced with the much better canny_tool class
00004 // canny will be deprecated in the next release
00005 
00006 #ifndef CANNY_H
00007 #define CANNY_H
00008 
00009 #include <iostream> 
00010 #include <fstream>
00011 #include <cmath>
00012 #include "cartesian.h"
00013 #include "image.h"
00014 #include "image_draw.h"
00015 
00016 namespace mimas {
00017   
00018 // $Header: /cvs/mimas2/include/canny.h,v 1.1.1.1 2005/08/09 15:37:45 engmb Exp $
00019 
00020 /* Scale floating point magnitudes and angles to 8 bits */
00021 // #define ORI_SCALE 40.0
00022 #define MAG_SCALE 20.0
00023 
00024 template<typename T>
00025 class canny : public object
00026             //perhaps public virtual drawable one day
00027 {
00028 protected:
00029     float norm (float x, float y);
00030     float gauss(float x, float sigma);
00031     float dGauss (float x, float sigma);
00032     float meanGauss (float x, float sigma);
00033     void separable_convolution( const image< int > &im,
00034                                 const std::vector< float > &gau,
00035                                 image< float > &smx,
00036                                 image< float > &smy );
00037     void dxy_separable_convolution( const std::vector< float > &dgau,
00038                                     const image< float > &smx,
00039                                     const image< float > &smy,
00040                                     image< float > &dx,
00041                                     image< float > &dy );
00042     void nonmax_suppress( const image< float > &dx,
00043                           const image< float > &dy,
00044                           image< float > &mag );
00045     void estimate_thresh( const image< float > &mag, int fringe );
00046     float distancePointToPoint(int ax, int ay, int bx, int by);
00047     float distancePointToLine(int ax, int ay, int bx, int by, int px, int py);
00048     void hysteresis( const image< float > &mag, image< int > &im,
00049                      int fringe );
00050     int trace( const image< float > &mag, image< int > &im, int i, int j);
00051 
00052     /*
00053     image<int> ori, mag, im;
00054     image<int> traced;
00055     image<float> smx, smy, dx, dy; */
00056 
00057 
00058 public:
00059     float ratio; /* Fraction of pixels that should be above the HIGH threshold */
00060     float sd; /* Gaussian standard deviation */
00061     int highthres, lowthres; /* high and low thresholds */
00062     bool autothreshold; /* autothresholding */
00063 
00064     void setCannyPixHighThresRatio(double tempval);
00065     double getCannyPixHighThresRatio(void);
00066     void setCannyStdDev(double tempval);
00067     double getCannyStdDev(void);
00068     void setCannyHighThres(int tempval);
00069     int getCannyHighThres(void);
00070     void setCannyLowThres(int tempval);
00071     int getCannyLowThres(void);
00072     void setCannyAutoThres(bool tempval);
00073     bool getCannyAutoThres(void);
00074     void getEdges(image<T> &inputImage, image<T> &outimage);
00075     void getEdges(image<T> &image);
00076 
00077 
00078     canny()
00079     {
00080         ratio=0.1;
00081         sd=1.0;
00082         highthres=127;
00083         lowthres=63;
00084         autothreshold=true;
00085     }
00086 };
00087 
00088 
00089 template<typename T>
00090 void canny<T>::setCannyPixHighThresRatio(double tempval)
00091 {
00092     ratio=tempval;
00093 }
00094 
00095 template<typename T>
00096 double canny<T>::getCannyPixHighThresRatio(void)
00097 {
00098     return ratio;
00099 }
00100 
00101 template<typename T>
00102 void canny<T>::setCannyStdDev(double tempval)
00103 {
00104     sd=tempval;
00105 }
00106 
00107 template<typename T>
00108 double canny<T>::getCannyStdDev(void)
00109 {
00110     return sd;
00111 }
00112 
00113 template<typename T>
00114 void canny<T>::setCannyHighThres(int tempval)
00115 {
00116     highthres=tempval;
00117 }
00118 
00119 template<typename T>
00120 int canny<T>::getCannyHighThres(void)
00121 {
00122     return highthres;
00123 }
00124 
00125 template<typename T>
00126 void canny<T>::setCannyLowThres(int tempval)
00127 {
00128     lowthres=tempval;
00129 }
00130 
00131 template<typename T>
00132 int canny<T>::getCannyLowThres(void)
00133 {
00134     return lowthres;
00135 }
00136 
00137 
00138 template<typename T>
00139 void canny<T>::setCannyAutoThres(bool tempval)
00140 {
00141     autothreshold=tempval;
00142 }
00143 
00144 template<typename T>
00145 bool canny<T>::getCannyAutoThres(void)
00146 {
00147     return autothreshold;
00148 }
00149 
00150 template<typename T>
00151 float canny<T>::norm (float x, float y)
00152 {
00153     return (float) sqrt ( (double)(x*x + y*y) );
00154 }
00155 
00156 template<typename T>
00157 void canny<T>::getEdges(image<T> &inputImage, image<T> &outimage)
00158 {
00159     std::vector< float > gau, dgau;
00160 
00161     image< int > im( inputImage );
00162 
00163     /* Create a Gaussian and a derivative of Gaussian filter mask */
00164     while ( true ) {
00165         int i = gau.size();
00166         gau.push_back( meanGauss ((float)i, sd) );
00167         if (gau[gau.size()-1] < 0.005)
00168           break;
00169         dgau.push_back( dGauss ((float)i, sd) );
00170     }
00171 
00172     //cout << "Smoothing with a Gaussian (width = %d) ..." << std::endl;
00173     /* Convolution of source image with a Gaussian in X and Y directions  */
00174     image< float > smx, smy;
00175     separable_convolution( im, gau, smx, smy );
00176 
00177     image< float > dx, dy;
00178     /* Now convolve smoothed data with a derivative */
00179     //cout << "Convolution with the derivative of a Gaussian..." << std::endl;
00180     dxy_separable_convolution( dgau, smx, smy, dx, dy );
00181 
00182     image< float > mag; mag.init( im.getWidth(), im.getHeight() );
00183 
00184     /* Non-maximum suppression - edge pixels should be a local max */
00185     nonmax_suppress( dx, dy, mag );
00186     hysteresis( mag, im, gau.size() / 2 );
00187 
00188     outimage.init(im.getWidth(),im.getHeight());
00189     for ( int i=1; i<im.getHeight()-1; i++)
00190         for ( int j=1; j<im.getWidth()-1; j++)
00191           outimage.pixel(j,i)=im.pixel(j,i);
00192     
00193     // set boundaries to zero due to an artefact of this implement of Canny
00194     drawLine(outimage,0,0,outimage.getWidth()-1,0,T(0));
00195     drawLine(outimage, outimage.getWidth()-1,0,outimage.getWidth()-1,outimage.getHeight()-1,T(0));
00196     drawLine(outimage, 0,outimage.getHeight()-1, outimage.getWidth()-1,
00197      outimage.getHeight()-1,T(0));
00198     drawLine(outimage,0,0,0,outimage.getHeight()-1,T(0));
00199     
00200 
00201     //dx.clear();
00202     //dy.clear();
00203 }
00204 
00205 template<typename T>
00206 void canny<T>::getEdges(image<T> &image)
00207 {
00208     getEdges(image,image);
00209 }
00210   
00211  /*      Gaussian        */
00212 template<typename T>
00213 float canny<T>::gauss(float x, float sigma)
00214 {
00215     float xx;
00216 
00217     if (sigma == 0) return 0.0;
00218     xx = (float)exp((double) ((-x*x)/(2*sigma*sigma)));
00219     return xx;
00220 }
00221 
00222 template<typename T>
00223 float canny<T>::meanGauss (float x, float sigma)
00224 {
00225     float z;
00226 
00227     z = (gauss(x,sigma)+gauss(x+0.5,sigma)+gauss(x-0.5,sigma))/3.0;
00228     z = z/(M_PI*2.0*sigma*sigma);
00229     return z;
00230 }
00231 
00232 /*      First derivative of Gaussian    */
00233 template<typename T>
00234 float canny<T>::dGauss (float x, float sigma)
00235 {
00236     return -x/(sigma*sigma) * gauss(x, sigma);
00237 }
00238 
00239 
00240 
00241 /*      HYSTERESIS thersholding of edge pixels. Starting at pixels with a
00242   value greater than the HIGH threshold, trace a connected sequence
00243   of pixels that have a value greater than the LOW threhsold.        */
00244 
00245 template<typename T>
00246 void canny<T>::hysteresis ( const image< float > &mag, image< int > &im,
00247                             int fringe )
00248 {
00249 
00250     //cout << "Beginning hysteresis thresholding..." << std::endl;
00251     for ( int i=0; i<im.getHeight(); i++)
00252         for ( int j=0; j<im.getWidth(); j++)
00253           im.pixel(j,i)=0;
00254 
00255 
00256     if ((autothreshold) || (highthres<lowthres))
00257     {
00258       estimate_thresh( mag, fringe );
00259         //cout << "Estimated hysteresis thresholds (from image): highthres=" << highthres << " lowthres=" << lowthres << std::endl;
00260     }
00261 
00262 
00263     /* For each edge with a magnitude above the high threshold, begin
00264       tracing edge pixels that are above the low threshold.                */
00265 
00266     for ( int i=0; i<im.getHeight(); i++)
00267         for ( int j=0; j<im.getWidth(); j++)
00268             if (mag.pixel(j,i) * MAG_SCALE >= highthres)
00269               trace ( mag, im, i, j);
00270     return; /*************************************************/
00271 }
00272 
00273 
00274 template<typename T>
00275 float canny<T>::distancePointToPoint(int ax, int ay, int bx, int by)
00276 {
00277     return(sqrt( ((bx-ax)*(bx-ax)) + ((by-ay)*(by-ay)) ));
00278 }
00279 
00280 /* Formula from comp.graphics.algorithms FAQ */
00281 template<typename T>
00282 float canny<T>::distancePointToLine(int ax, int ay, int bx, int by, int px, int py)
00283 {
00284     return(fabs( ((ay-py)*(bx-ax) - (ax-px)*(by-ay)) / distancePointToPoint (ax,ay,bx,by)  ));
00285 }
00286 
00287 
00288 /*******************************/
00289 
00290 /*      TRACE - recursively trace edge pixels that have a threshold > the low
00291   edge threshold, continuing from the pixel at (i,j).                     */
00292 
00293 
00294 
00295 template<typename T>
00296 int canny<T>::trace ( const image< float > &mag, image< int > &im, int i, int j)
00297 {
00298     char flag = 0;
00299 
00300     if (im.pixel(j,i) == 0)
00301     {
00302       im.pixel(j,i) = 255;
00303         flag=0;
00304         for ( int n= -1; n<=1; n++)
00305         {
00306             for ( int m= -1; m<=1; m++)
00307             {
00308                 if (n==0 && m==0) continue;
00309                 if (((i+n)>=0) && ((i+n)<mag.getHeight()) &&
00310                     ((j+m)>=0) && ((j+m)<mag.getWidth())) {
00311                   if (mag.pixel(j+m, i+n) * MAG_SCALE >=lowthres)
00312                     {
00313                       if (trace( mag, im, i+n, j+m ))
00314                         {
00315                           flag=1;
00316                           break;
00317                         }
00318                     }
00319                 };
00320             }
00321             if (flag) break;
00322         }
00323         return(1);
00324     }
00325     return(0);
00326 }
00327 
00328 
00329 
00330 
00331 
00332 
00333 template<typename T>
00334 void canny<T>::separable_convolution( const image< int > &im,
00335                                       const std::vector< float > &gau,
00336                                       image< float > &smx,
00337                                       image< float > &smy )
00338 {
00339     int
00340       nr = im.getHeight(),
00341       nc = im.getWidth();
00342     smx.init( nc, nr );
00343     smy.init( nc, nr );
00344 
00345     for ( int i=0; i<nr; i++)
00346         for ( int j=0; j<nc; j++)
00347         {
00348           float x = gau[0] * im.pixel(j,i);
00349           float y = gau[0] * im.pixel(j,i);
00350           for ( int k=1; k<(signed)gau.size(); k++)
00351             {
00352               int
00353                 I1 = (i+k)%nr,
00354                 I2 = (i-k+nr)%nr;
00355               y += gau[k]*im.pixel(j,I1) + gau[k]*im.pixel(j,I2);
00356               int
00357                 J1 = (j+k)%nc,
00358                 J2 = (j-k+nc)%nc;
00359               x += gau[k]*im.pixel(J1,i) + gau[k]*im.pixel(J2,i);
00360             }
00361             smx.pixel(j,i) = x; smy.pixel(j,i) = y;
00362         }
00363 }
00364 
00365 template<typename T>
00366   void canny<T>::dxy_separable_convolution( const std::vector< float > &dgau,
00367                                             const image< float > &smx,
00368                                             const image< float > &smy,
00369                                             image< float > &dx,
00370                                             image< float > &dy )
00371   {
00372     int
00373       nr = smx.getHeight(),
00374       nc = smx.getWidth();
00375     
00376     dx.init( nc, nr );
00377     dy.init( nc, nr );
00378 
00379     for ( int i=0; i<nr; i++)
00380         for ( int j=0; j<nc; j++)
00381         {
00382           float
00383             x = 0.0,
00384             y = 0.0;
00385           for ( int k=1; k<(signed)dgau.size(); k++)
00386             {
00387               {
00388                 int
00389                   I1 = (i+k)%nr,
00390                   I2 = (i-k+nr)%nr;
00391                 y += -dgau[k]*smy.pixel(j,I1) + dgau[k]*smy.pixel(j,I2);
00392               };
00393               {
00394                 int
00395                   I1 = (j+k)%nc,
00396                   I2 = (j-k+nc)%nc;
00397                 x += -dgau[k]*smx.pixel(I1,i) + dgau[k]*smx.pixel(I2,i);
00398               }
00399             }
00400           dx.pixel(j,i) = x;
00401           dy.pixel(j,i) = y;
00402         }
00403 }
00404 
00405 template<typename T>
00406 void canny<T>::nonmax_suppress( const image< float > &dx,
00407                                 const image< float > &dy,
00408                                 image< float > &mag )
00409 {
00410 
00411   // image< int > ori; ori.init( mag.getWidth(), mag.getHeight() );
00412 
00413     for ( int i=1; i<mag.getHeight()-1; i++)
00414     {
00415         for ( int j=1; j<mag.getWidth()-1; j++)
00416         {
00417           mag.pixel(j,i)=0;
00418 
00419             /* Treat the x and y derivatives as components of a vector */
00420           float
00421             xc = dx.pixel(j,i),
00422             yc = dy.pixel(j,i);
00423             if ( ( fabs( xc ) + fabs( yc ) ) * MAG_SCALE < lowthres )
00424               continue;
00425 
00426             float g  = norm (xc, yc);
00427 
00428             float xx, yy, g2, g1, g3, g4;
00429 
00430             /* Follow the gradient direction, as indicated by the direction of
00431                the vector (xc, yc); retain pixels that are a local maximum. */
00432 
00433             if (fabs(yc) > fabs(xc))
00434             {
00435 
00436                 /* The Y component is biggest, so gradient direction is basically UP/DOWN */
00437                 xx = fabs(xc)/fabs(yc);
00438                 yy = 1.0;
00439 
00440                 g2 = norm (dx.pixel(j,i-1), dy.pixel(j,i-1));
00441                 g4 = norm (dx.pixel(j,i+1), dy.pixel(j,i+1));
00442                 if (xc*yc > 0.0)
00443                 {
00444                     g3 = norm (dx.pixel(j+1,i+1), dy.pixel(j+1,i+1));
00445                     g1 = norm (dx.pixel(j-1,i-1), dy.pixel(j-1,i-1));
00446                 }
00447                 else
00448                 {
00449                     g3 = norm (dx.pixel(j-1,i+1), dy.pixel(j-1,i+1));
00450                     g1 = norm (dx.pixel(j+1,i-1), dy.pixel(j+1,i-1));
00451                 }
00452 
00453             } else
00454             {
00455 
00456                 /* The X component is biggest, so gradient direction is basically LEFT/RIGHT */
00457                 xx = fabs(yc)/fabs(xc);
00458                 yy = 1.0;
00459 
00460                 g2 = norm (dx.pixel(j+1,i), dy.pixel(j+1,i));
00461                 g4 = norm (dx.pixel(j-1,i), dy.pixel(j-1,i));
00462                 if (xc*yc > 0.0)
00463                 {
00464                     g3 = norm (dx.pixel(j-1,i-1), dy.pixel(j-1,i-1));
00465                     g1 = norm (dx.pixel(j+1,i+1), dy.pixel(j+1,i+1));
00466                 }
00467                 else
00468                 {
00469                     g1 = norm (dx.pixel(j+1,i-1), dy.pixel(j+1,i-1));
00470                     g3 = norm (dx.pixel(j-1,i+1), dy.pixel(j-1,i+1));
00471                 }
00472             }
00473 
00474             /* Compute the interpolated value of the gradient magnitude */
00475             if ( (g > (xx*g1 + (yy-xx)*g2)) &&
00476                     (g > (xx*g3 + (yy-xx)*g4)) )
00477             {
00478               if (g*MAG_SCALE <= 255)
00479                 mag.pixel(j,i) = g;
00480               else
00481                 mag.pixel(j,i) = 255 / MAG_SCALE;
00482               // ori.setPixel(j,i,(T)(atan2 (yc, xc) * ORI_SCALE));
00483             } else
00484             {
00485               mag.pixel(j,i)= 0;
00486               // ori.setPixel(j,i,0);
00487             }
00488 
00489         }
00490         //  cerr << i << " " << j << std::endl;
00491 
00492     }
00493 }
00494 
00495 template<typename T>
00496 void canny<T>::estimate_thresh( const image< float > &mag, int fringe )
00497 {
00498     int i,j,k, hist[256], count;
00499 
00500     /* Build a histogram of the magnitude image. */
00501     for (k=0; k<256; k++) hist[k] = 0;
00502 
00503     for (i=fringe; i<mag.getHeight()-fringe; i++)
00504         for (j=fringe; j<mag.getWidth()-fringe; j++)
00505           hist[(int)(mag.pixel(j,i)*MAG_SCALE)]++;
00506 
00507     /* The high threshold should be > 80 or 90% of the pixels
00508       j = (int)(ratio*mag->info->nr*mag->info->nc);
00509     */
00510     j = mag.getHeight();
00511     if (j<mag.getWidth()) j = mag.getWidth();
00512     j = (int)(0.9*j);
00513     k = 255;
00514 
00515     count = hist[255];
00516     while (count < j)
00517     {
00518         k--;
00519         if (k<0) break;
00520         count += hist[k];
00521     }
00522     highthres = k;
00523 
00524     i=0;
00525     while (hist[i]==0) i++;
00526 
00527     lowthres = (int)((highthres+i)/2.0);
00528 }
00529 
00530 }
00531 
00532 #endif /* CANNY_H */
00533 

[GNU/Linux] [Qt] [Mesa] [STL] [Lapack] [Boost] [Magick++] [Xalan-C and Xerces-C] [doxygen] [graphviz] [FFTW] [popt] [xine] [Gnuplot] [gnu-arch] [gcc] [gstreamer] [autoconf/automake/make] [freshmeat.net] [opensource.org] [sourceforge.net] [MMVL]
mimas 2.1 - Copyright Mon Oct 30 11:31:16 2006, Bala Amavasai, Stuart Meikle, Arul Selvan, Fabio Caparrelli, Jan Wedekind, Manuel Boissenin, ...