00001
00002
00003
00004
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
00019
00020
00021
00022 #define MAG_SCALE 20.0
00023
00024 template<typename T>
00025 class canny : public object
00026
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
00054
00055
00056
00057
00058 public:
00059 float ratio;
00060 float sd;
00061 int highthres, lowthres;
00062 bool autothreshold;
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
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
00173
00174 image< float > smx, smy;
00175 separable_convolution( im, gau, smx, smy );
00176
00177 image< float > dx, dy;
00178
00179
00180 dxy_separable_convolution( dgau, smx, smy, dx, dy );
00181
00182 image< float > mag; mag.init( im.getWidth(), im.getHeight() );
00183
00184
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
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
00202
00203 }
00204
00205 template<typename T>
00206 void canny<T>::getEdges(image<T> &image)
00207 {
00208 getEdges(image,image);
00209 }
00210
00211
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
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
00242
00243
00244
00245 template<typename T>
00246 void canny<T>::hysteresis ( const image< float > &mag, image< int > &im,
00247 int fringe )
00248 {
00249
00250
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
00260 }
00261
00262
00263
00264
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
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
00291
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
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
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
00431
00432
00433 if (fabs(yc) > fabs(xc))
00434 {
00435
00436
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
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
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
00483 } else
00484 {
00485 mag.pixel(j,i)= 0;
00486
00487 }
00488
00489 }
00490
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
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
00508
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
00533