/** \file * \brief Canny Edge Detector * * See Copyright Notice in im_lib.h * $Id: im_canny.cpp,v 1.1 2008/10/17 06:16:33 scuri Exp $ */ #include #include "im_process_loc.h" #include #include #include /* Scale floating point magnitudes to 8 bits */ static float MAG_SCALE; /* Biggest possible filter mask */ #define MAX_MASK_SIZE 100 static float ** f2d (int nr, int nc); static float gauss(float x, float sigma); static float dGauss (float x, float sigma); static float meanGauss (float x, float sigma); static void seperable_convolution (const imImage* im, float *gau, int width, float **smx, float **smy); static void dxy_seperable_convolution (float** im, int nr, int nc, float *gau, int width, float **sm, int which); static void nonmax_suppress (float **dx, float **dy, imImage* mag); void imProcessCanny(const imImage* im, imImage* NewImage, float stddev) { int width = 1; float **smx,**smy; float **dx,**dy; int i; float gau[MAX_MASK_SIZE], dgau[MAX_MASK_SIZE]; /* Create a Gaussian and a derivative of Gaussian filter mask */ for(i=0; iheight, im->width); smy = f2d (im->height, im->width); /* Convolution of source image with a Gaussian in X and Y directions */ seperable_convolution (im, gau, width, smx, smy); MAG_SCALE = 0; /* Now convolve smoothed data with a derivative */ dx = f2d (im->height, im->width); dxy_seperable_convolution (smx, im->height, im->width, dgau, width, dx, 1); free(smx[0]); free(smx); dy = f2d (im->height, im->width); dxy_seperable_convolution (smy, im->height, im->width, dgau, width, dy, 0); free(smy[0]); free(smy); if (MAG_SCALE) MAG_SCALE = 255.0f/(1.4142f*MAG_SCALE); /* Non-maximum suppression - edge pixels should be a local max */ nonmax_suppress (dx, dy, NewImage); free(dx[0]); free(dx); free(dy[0]); free(dy); } static float norm (float x, float y) { return (float) sqrt ( (double)(x*x + y*y) ); } static float ** f2d (int nr, int nc) { float **x, *y; int i; x = (float **)calloc ( nr, sizeof (float *) ); if (!x) return NULL; y = (float *)calloc ( nr*nc, sizeof (float) ); if (!y) return NULL; for (i=0; idata[0]; nr = im->height; nc = im->width; for (i=0; iwidth + j]; y = gau[0] * im_data[i*im->width + j]; for (k=1; kwidth + j] + gau[k]*im_data[I2*im->width + j]; I1 = (j+k)%nc; I2 = (j-k+nc)%nc; x += gau[k]*im_data[i*im->width + I1] + gau[k]*im_data[i*im->width + I2]; } smx[i][j] = x; smy[i][j] = y; } } } static void dxy_seperable_convolution (float** im, int nr, int nc, float *gau, int width, float **sm, int which) { int i,j,k, I1, I2; float x; for (i=0; i MAG_SCALE) MAG_SCALE = x; } } } static unsigned char tobyte(float x) { if (x > 255) return 255; return (unsigned char)x; } static void nonmax_suppress (float **dx, float **dy, imImage* mag) { int i,j; float xx, yy, g2, g1, g3, g4, g, xc, yc; unsigned char* mag_data = (unsigned char*)mag->data[0]; for (i=1; iheight-1; i++) { for (j=1; jwidth-1; j++) { /* Treat the x and y derivatives as components of a vector */ xc = dx[i][j]; yc = dy[i][j]; if (fabs(xc)<0.01 && fabs(yc)<0.01) continue; g = norm (xc, yc); /* Follow the gradient direction, as indicated by the direction of the vector (xc, yc); retain pixels that are a local maximum. */ if (fabs(yc) > fabs(xc)) { /* The Y component is biggest, so gradient direction is basically UP/DOWN */ xx = (float)(fabs(xc)/fabs(yc)); yy = 1.0; g2 = norm (dx[i-1][j], dy[i-1][j]); g4 = norm (dx[i+1][j], dy[i+1][j]); if (xc*yc > 0.0) { g3 = norm (dx[i+1][j+1], dy[i+1][j+1]); g1 = norm (dx[i-1][j-1], dy[i-1][j-1]); } else { g3 = norm (dx[i+1][j-1], dy[i+1][j-1]); g1 = norm (dx[i-1][j+1], dy[i-1][j+1]); } } else { /* The X component is biggest, so gradient direction is basically LEFT/RIGHT */ xx = (float)(fabs(yc)/fabs(xc)); yy = 1.0; g2 = norm (dx[i][j+1], dy[i][j+1]); g4 = norm (dx[i][j-1], dy[i][j-1]); if (xc*yc > 0.0) { g3 = norm (dx[i-1][j-1], dy[i-1][j-1]); g1 = norm (dx[i+1][j+1], dy[i+1][j+1]); } else { g1 = norm (dx[i-1][j+1], dy[i-1][j+1]); g3 = norm (dx[i+1][j-1], dy[i+1][j-1]); } } /* Compute the interpolated value of the gradient magnitude */ if ( (g > (xx*g1 + (yy-xx)*g2)) && (g > (xx*g3 + (yy-xx)*g4)) ) { mag_data[i*mag->width + j] = tobyte(g*MAG_SCALE); } } } }