www.pudn.com > imageFeatures_Ver3_0_source.zip > canny.cpp
////////////////////////////////////////////////////////////////// // Canny.cpp // Original Code from: // Algorithms for Image Processing and Computer Vision // By J.R. Parker // Modified for VisSDK by: Travis A Udd 11/30/00 ////////////////////////////////////////////////////////////////// #include "stdafx.h" #include#include #include "canny.h" #include "profile.h" /* Scale floating point magnitudes and angles to 8 bits */ #define ORI_SCALE 40.0 #define MAG_SCALE 20.0 #define PI_f 3.14159f /* Biggest possible filter mask */ #define MAX_MASK_SIZE 20 ////////////////////////// // Travis A Udd 11/30/00 // Revised 6/02/03 Tyler Folsom // Canny Constructor ////////////////////////// Canny::Canny(CDefaultImage *imageSrc) { ratio = 0.1f; WIDTH = 0; m_pImage = imageSrc; } ////////////////////////////////////////////////////////////////// // CannyEdgeDetect() // Travis A Udd 11/30/00 // Function: Fills 2D buffer with image // and runs canny filter function on it. // result is edges found in image. ////////////////////////////////////////////////////////////////// void Canny::CannyEdgeDetect(CVisRGBAByteImage *featureImage) { PROFILE("CannyEdgeDetect"); // There is a memory leak somewhere! 06/02/03 Tyler Folsom imagec *im; int iWidth = m_pImage->Width(), iHeight = m_pImage->Height(), i, j, total; CVisRGBA temp; im=newimagec (iHeight,iWidth); //Convert the RGB image to grayscale and load it into buffer for(i=0 ; i Pixel(j,i); total = (temp.R()+temp.G()+temp.B()) / 3; im->data[i][j] = total; } // Do canny filter on image CannyFilter(im, HighTh, LowTh, Gaussian); // Draw image for(i=0 ; i data[i][j]) featureImage->Pixel(j,i) = m_pImage->Pixel(j,i); else featureImage->Pixel(j,i).SetRGB(255,0,0); } freeimagec (im); } float ** Canny::f2d (int nr, int nc) { float **x; int i; x = (float **)calloc ( nr, sizeof (float *) ); if (x == 0) { fprintf (stderr, "Out of storage: F2D.\n"); exit (1); } for (i=0; i info = (struct header *)malloc( sizeof(struct header) ); if (!(x->info)) { printf ("Out of storage in NEWIMAGE.\n"); return 0; } x->info->nr = nr; x->info->nc = nc; x->info->oi = x->info->oj = 0; /* Allocate the pixel array */ x->data = (unsigned char **)malloc(sizeof(unsigned char *)*nr); /* Pointers to rows */ if (!(x->data)) { printf ("Out of storage in NEWIMAGE.\n"); return 0; } for (i=0; i data[i] = (unsigned char *)malloc(nc*sizeof(unsigned char)); if (x->data[i]==0) { printf ("Out of storage. Newimage - row %d\n", i); exit(1); } } return x; } void Canny::freeimagec (struct imagec *z) { /* Free the storage associated with the image Z */ int i; if (z != 0) { for (i=0; i info->nr; i++) free (z->data[i]); free (z->info); free (z->data); free (z); } } ////////////////////////////////////////////////////////////////// // CannyFilter() // Original Code from: // Algorithms for Image Processing and Computer Vision // By J.R. Parker // Modified for VisSDK by: Travis A Udd 11/30/00 // Function: Gets original image im and returns im canny filtered // for display. ////////////////////////////////////////////////////////////////// void Canny::CannyFilter(imagec *im,int low,int high,double gaussian) { PROFILE("CannyFilter"); int i,j; float s; imagec *magim, *oriim; s=(float)gaussian; magim = newimagec (im->info->nr, im->info->nc); if (magim == NULL) { printf ("Out of storage: Magnitude\n"); exit (1); } oriim = newimagec (im->info->nr, im->info->nc); if (oriim == NULL) { printf ("Out of storage: Orientation\n"); exit (1); } /* Apply the filter */ canny (s, im, magim, oriim); /* Hysteresis thresholding of edge pixels */ hysteresis (high, low, im, magim, oriim); for (i=0; i info->nc; j++) im->data[i][j] = 255; for (i=im->info->nr-1; i>im->info->nr-1-WIDTH; i--) for (j=0; j info->nc; j++) im->data[i][j] = 255; for (i=0; i info->nr; i++) for (j=0; j data[i][j] = 255; for (i=0; i info->nr; i++) for (j=im->info->nc-WIDTH-1; j info->nc; j++) im->data[i][j] = 255; freeimagec(magim); freeimagec(oriim); } float Canny::norm (float x, float y) { return (float) sqrt ( (double)(x*x + y*y) ); } void Canny::canny (float s, imagec* im, imagec* mag, imagec* ori) { int width; float **smx,**smy; float **dx,**dy; int i,j,n; float gau[MAX_MASK_SIZE], dgau[MAX_MASK_SIZE], z; /* Create a Gaussian and a derivative of Gaussian filter mask */ for(i=0; i info->nr, im->info->nc); smy = f2d (im->info->nr, im->info->nc); /* Convolution of source image with a Gaussian in X and Y directions */ seperable_convolution (im, gau, width, smx, smy); /* Now convolve smoothed data with a derivative */ printf ("Convolution with the derivative of a Gaussian...\n"); dx = f2d (im->info->nr, im->info->nc); dxy_seperable_convolution (smx, im->info->nr, im->info->nc, dgau, width, dx, 1); free(smx[0]); free(smx); dy = f2d (im->info->nr, im->info->nc); dxy_seperable_convolution (smy, im->info->nr, im->info->nc, dgau, width, dy, 0); free(smy[0]); free(smy); /* Create an image of the norm of dx,dy */ for (i=0; i info->nr; i++) for (j=0; j info->nc; j++) { z = norm (dx[i][j], dy[i][j]); mag->data[i][j] = (unsigned char)(z*MAG_SCALE); } /* Non-maximum suppression - edge pixels should be a local max */ nonmax_suppress (dx, dy, (int)im->info->nr, (int)im->info->nc, mag, ori); free(dx[0]); free(dx); free(dy[0]); free(dy); } /* Gaussian */ float Canny::gauss(float x, float sigma) { float xx; if (sigma == 0) return 0.0; xx = (float)exp((double) ((-x*x)/(2*sigma*sigma))); return xx; } float Canny::meanGauss (float x, float sigma) { float z; z = (gauss(x,sigma)+gauss(x+0.5,sigma)+gauss(x-0.5,sigma))/3.0; z = z/(PI_f*2.0*sigma*sigma); return z; } /* First derivative of Gaussian */ float Canny::dGauss (float x, float sigma) { return -x/(sigma*sigma) * gauss(x, sigma); } /* HYSTERESIS thersholding of edge pixels. Starting at pixels with a value greater than the HIGH threshold, trace a connected sequence of pixels that have a value greater than the LOW threhsold. */ void Canny::hysteresis (int high, int low, imagec* im, imagec* mag, imagec* oriim) { int i,j; printf ("Beginning hysteresis thresholding...\n"); for (i=0; i info->nr; i++) for (j=0; j info->nc; j++) im->data[i][j] = 0; if (high info->nr; i++) for (j=0; j info->nc; j++) if (mag->data[i][j] >= high) trace (i, j, low, im, mag, oriim); /* Make the edge black (to be the same as the other methods) */ for (i=0; i info->nr; i++) for (j=0; j info->nc; j++) if (im->data[i][j] == 0) im->data[i][j] = 255; else im->data[i][j] = 0; } /* Check that a pixel index is in range. Return TRUE(1) if so. */ int Canny::range (imagec* im, int i, int j) { if ((i<0) || (i>=im->info->nr)) return 0; if ((j<0) || (j>=im->info->nc)) return 0; return 1; } /* TRACE - recursively trace edge pixels that have a threshold > the low edge threshold, continuing from the pixel at (i,j). */ int Canny::trace (int i, int j, int low, imagec* im,imagec* mag, imagec* ori) { int n,m; char flag = 0; if (im->data[i][j] == 0) { im->data[i][j] = 255; flag=0; for (n= -1; n<=1; n++) { for(m= -1; m<=1; m++) { if (i==0 && m==0) continue; if (range(mag, i+n, j+m) && mag->data[i+n][j+m] >= low) if (trace(i+n, j+m, low, im, mag, ori)) { flag=1; break; } } if (flag) break; } return(1); } return(0); } void Canny::seperable_convolution (imagec* im, float *gau, int width, float **smx, float **smy) { int i,j,k, I1, I2, nr, nc; float x, y; nr = im->info->nr; nc = im->info->nc; for (i=0; i data[i][j]; y = gau[0] * im->data[i][j]; for (k=1; k data[I1][j] + gau[k]*im->data[I2][j]; I1 = (j+k)%nc; I2 = (j-k+nc)%nc; x += gau[k]*im->data[i][I1] + gau[k]*im->data[i][I2]; } smx[i][j] = x; smy[i][j] = y; } } void Canny::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 info->nr-1; i++) { for (j=1; j info->nc-1; j++) { mag->data[i][j] = 0; /* 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 = 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 = 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)) ) { if (g*MAG_SCALE <= 255) mag->data[i][j] = (unsigned char)(g*MAG_SCALE); else mag->data[i][j] = 255; ori->data[i][j] = atan2 (yc, xc) * ORI_SCALE; } else { mag->data[i][j] = 0; ori->data[i][j] = 0; } } } } void Canny::estimate_thresh (imagec* mag, int *hi, int *low) { int i,j,k, hist[256], count; /* Build a histogram of the magnitude image. */ for (k=0; k<256; k++) hist[k] = 0; for (i=WIDTH; i info->nr-WIDTH; i++) for (j=WIDTH; j info->nc-WIDTH; j++) hist[mag->data[i][j]]++; /* The high threshold should be > 80 or 90% of the pixels j = (int)(ratio*mag->info->nr*mag->info->nc); */ j = mag->info->nr; if (j info->nc) j = mag->info->nc; j = (int)(0.9*j); k = 255; count = hist[255]; while (count < j) { k--; if (k<0) break; count += hist[k]; } *hi = k; i=0; while (hist[i]==0) i++; *low = (*hi+i)/2.0; }