00001 /*!@file BeoSub/test-hough.C */ 00002 00003 // //////////////////////////////////////////////////////////////////// // 00004 // The iLab Neuromorphic Vision C++ Toolkit - Copyright (C) 2000-2005 // 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: 00034 // $HeadURL: svn://isvn.usc.edu/software/invt/trunk/saliency/src/BeoSub/test-hough.C $ 00035 // $Id: test-hough.C 14376 2011-01-11 02:44:34Z pez $ 00036 // 00037 00038 #ifndef BEOSUB_TEST_HOUGH_C_DEFINED 00039 #define BEOSUB_TEST_HOUGH_C_DEFINED 00040 00041 #include "GUI/XWindow.H" 00042 //CAMERA STUFF 00043 #include "Image/Image.H" 00044 #include "Image/Pixels.H" 00045 00046 #include "Component/ModelManager.H" 00047 #include "Devices/FrameGrabberFactory.H" 00048 #include "Util/Timer.H" 00049 #include "Util/Types.H" 00050 #include "Util/log.H" 00051 #include "Image/ColorOps.H" 00052 #include "Image/Image.H" 00053 #include "Image/MathOps.H" 00054 #include "Image/DrawOps.H" 00055 #include "Image/FilterOps.H" 00056 #include "Image/Transforms.H" 00057 #include "Raster/Raster.H" 00058 #include "rutz/shared_ptr.h" 00059 #include "BeoSub/hysteresis.H" 00060 #include "VFAT/segmentImageTrackMC.H" 00061 #include <cstdio> 00062 #include <cstdlib> 00063 #include <cstring> 00064 #include <iostream> //needed for segmentImageTrackMC! 00065 #include <math.h> 00066 #include <list> 00067 #include "Image/MatrixOps.H" 00068 00069 #include "MBARI/Geometry2D.H" 00070 #include "rutz/compat_cmath.h" // for isnan() 00071 00072 using std::cout; 00073 using std::endl; 00074 using std::list; 00075 00076 //END CAMERA STUFF 00077 //canny 00078 #define BOOSTBLURFACTOR 90.0 00079 #define FREE_ARG char* 00080 #define PI 3.14 00081 00082 XWindow *xwin; 00083 XWindow *xwin2; 00084 XWindow *xwin3; 00085 00086 void gaussian_smooth(unsigned char *image, int rows, int cols, float sigma, 00087 short int **smoothedim); 00088 void make_gaussian_kernel(float sigma, float **kernel, int *windowsize); 00089 void derrivative_x_y(short int *smoothedim, int rows, int cols, 00090 short int **delta_x, short int **delta_y); 00091 void magnitude_x_y(short int *delta_x, short int *delta_y, int rows, int cols, 00092 short int **magnitude); 00093 void radian_direction(short int *delta_x, short int *delta_y, int rows, 00094 int cols, float **dir_radians, int xdirtag, int ydirtag); 00095 void radian_direction(short int *delta_x, short int *delta_y, int rows, 00096 int cols, float **dir_radians, int xdirtag, int ydirtag); 00097 double angle_radians(double x, double y); 00098 00099 std::vector <LineSegment2D> houghTransform(Image<byte> &inputImage, float thetaRes, float dRes, int threshold, Image< PixRGB<byte> > &output) ; 00100 00101 /******************************************************************************* 00102 * PROCEDURE: canny 00103 * PURPOSE: To perform canny edge detection. 00104 * NAME: Mike Heath 00105 * DATE: 2/15/96 00106 //Pradeep: returns the centroid of the "white" pixels 00107 *******************************************************************************/ 00108 int canny(unsigned char *image, int rows, int cols, float sigma, 00109 float tlow, float thigh, unsigned char **edge, char *fname) 00110 { 00111 FILE *fpdir=NULL; /* File to write the gradient image to. */ 00112 unsigned char *nms; /* Points that are local maximal magnitude. */ 00113 short int *smoothedim, /* The image after gaussian smoothing. */ 00114 *delta_x, /* The first devivative image, x-direction. */ 00115 *delta_y, /* The first derivative image, y-direction. */ 00116 *magnitude; /* The magnitude of the gadient image. */ 00117 float *dir_radians=NULL; /* Gradient direction image. */ 00118 00119 /**************************************************************************** 00120 * Perform gaussian smoothing on the image using the input standard 00121 * deviation. 00122 ****************************************************************************/ 00123 gaussian_smooth(image, rows, cols, sigma, &smoothedim); 00124 00125 00126 /**************************************************************************** 00127 * Compute the first derivative in the x and y directions. 00128 ****************************************************************************/ 00129 derrivative_x_y(smoothedim, rows, cols, &delta_x, &delta_y); 00130 00131 00132 00133 00134 /**************************************************************************** 00135 * This option to write out the direction of the edge gradient was added 00136 * to make the information available for computing an edge quality figure 00137 * of merit. 00138 ****************************************************************************/ 00139 if(fname != NULL){ 00140 /************************************************************************* 00141 * Compute the direction up the gradient, in radians that are 00142 * specified counteclockwise from the positive x-axis. 00143 *************************************************************************/ 00144 radian_direction(delta_x, delta_y, rows, cols, &dir_radians, -1, -1); 00145 00146 /************************************************************************* 00147 * Write the gradient direction image out to a file. 00148 *************************************************************************/ 00149 if((fpdir = fopen(fname, "wb")) == NULL){ 00150 fprintf(stderr, "Error opening the file %s for writing.\n", fname); 00151 exit(1); 00152 } 00153 fwrite(dir_radians, sizeof(float), rows*cols, fpdir); 00154 fclose(fpdir); 00155 free(dir_radians); 00156 } 00157 00158 /**************************************************************************** 00159 * Compute the magnitude of the gradient. 00160 ****************************************************************************/ 00161 magnitude_x_y(delta_x, delta_y, rows, cols, &magnitude); 00162 00163 /**************************************************************************** 00164 * Perform non-maximal suppression. 00165 ****************************************************************************/ 00166 if((nms = (unsigned char *) calloc(rows*cols,sizeof(unsigned char)))==NULL){ 00167 fprintf(stderr, "Error allocating the nms image.\n"); 00168 exit(1); 00169 } 00170 00171 non_max_supp(magnitude, delta_x, delta_y, rows, cols, nms); 00172 00173 /**************************************************************************** 00174 * Use hysteresis to mark the edge pixels. 00175 ****************************************************************************/ 00176 if((*edge=(unsigned char *)calloc(rows*cols,sizeof(unsigned char))) ==NULL){ 00177 fprintf(stderr, "Error allocating the edge image.\n"); 00178 exit(1); 00179 } 00180 //printf("\n%d %d %d %d %d %d\n", magnitude, nms, rows, cols, tlow, thigh); 00181 int centroid = apply_hysteresis(magnitude, nms, rows, cols, tlow, thigh, *edge); 00182 /**************************************************************************** 00183 * Free all of the memory that we allocated except for the edge image that 00184 * is still being used to store out result. 00185 ****************************************************************************/ 00186 free(smoothedim); 00187 free(delta_x); 00188 free(delta_y); 00189 free(magnitude); 00190 free(nms); 00191 return centroid; 00192 } 00193 00194 /******************************************************************************* 00195 * Procedure: radian_direction 00196 * Purpose: To compute a direction of the gradient image from component dx and 00197 * dy images. Because not all derriviatives are computed in the same way, this 00198 * code allows for dx or dy to have been calculated in different ways. 00199 * 00200 * FOR X: xdirtag = -1 for [-1 0 1] 00201 * xdirtag = 1 for [ 1 0 -1] 00202 * 00203 * FOR Y: ydirtag = -1 for [-1 0 1]' 00204 * ydirtag = 1 for [ 1 0 -1]' 00205 * 00206 * The resulting angle is in radians measured counterclockwise from the 00207 * xdirection. The angle points "up the gradient". 00208 *******************************************************************************/ 00209 void radian_direction(short int *delta_x, short int *delta_y, int rows, 00210 int cols, float **dir_radians, int xdirtag, int ydirtag) 00211 { 00212 int r, c, pos; 00213 float *dirim=NULL; 00214 double dx, dy; 00215 00216 /**************************************************************************** 00217 * Allocate an image to store the direction of the gradient. 00218 ****************************************************************************/ 00219 if((dirim = (float *) calloc(rows*cols, sizeof(float))) == NULL){ 00220 fprintf(stderr, "Error allocating the gradient direction image.\n"); 00221 exit(1); 00222 } 00223 *dir_radians = dirim; 00224 00225 for(r=0,pos=0;r<rows;r++){ 00226 for(c=0;c<cols;c++,pos++){ 00227 dx = (double)delta_x[pos]; 00228 dy = (double)delta_y[pos]; 00229 00230 if(xdirtag == 1) dx = -dx; 00231 if(ydirtag == -1) dy = -dy; 00232 00233 dirim[pos] = (float)angle_radians(dx, dy); 00234 } 00235 } 00236 } 00237 00238 /******************************************************************************* 00239 * FUNCTION: angle_radians 00240 * PURPOSE: This procedure computes the angle of a vector with components x and 00241 * y. It returns this angle in radians with the answer being in the range 00242 * 0 <= angle <2*PI. 00243 *******************************************************************************/ 00244 double angle_radians(double x, double y) 00245 { 00246 double xu, yu, ang; 00247 00248 xu = fabs(x); 00249 yu = fabs(y); 00250 00251 if((xu == 0) && (yu == 0)) return(0); 00252 00253 ang = atan(yu/xu); 00254 00255 if(x >= 0){ 00256 if(y >= 0) return(ang); 00257 else return(2*M_PI - ang); 00258 } 00259 else{ 00260 if(y >= 0) return(M_PI - ang); 00261 else return(M_PI + ang); 00262 } 00263 } 00264 00265 /******************************************************************************* 00266 * PROCEDURE: magnitude_x_y 00267 * PURPOSE: Compute the magnitude of the gradient. This is the square root of 00268 * the sum of the squared derivative values. 00269 * NAME: Mike Heath 00270 * DATE: 2/15/96 00271 *******************************************************************************/ 00272 void magnitude_x_y(short int *delta_x, short int *delta_y, int rows, int cols, 00273 short int **magnitude) 00274 { 00275 int r, c, pos, sq1, sq2; 00276 00277 /**************************************************************************** 00278 * Allocate an image to store the magnitude of the gradient. 00279 ****************************************************************************/ 00280 if((*magnitude = (short *) calloc(rows*cols, sizeof(short))) == NULL){ 00281 fprintf(stderr, "Error allocating the magnitude image.\n"); 00282 exit(1); 00283 } 00284 00285 for(r=0,pos=0;r<rows;r++){ 00286 for(c=0;c<cols;c++,pos++){ 00287 sq1 = (int)delta_x[pos] * (int)delta_x[pos]; 00288 sq2 = (int)delta_y[pos] * (int)delta_y[pos]; 00289 (*magnitude)[pos] = (short)(0.5 + sqrt((float)sq1 + (float)sq2)); 00290 } 00291 } 00292 00293 } 00294 00295 /******************************************************************************* 00296 * PROCEDURE: derrivative_x_y 00297 * PURPOSE: Compute the first derivative of the image in both the x any y 00298 * directions. The differential filters that are used are: 00299 * 00300 * -1 00301 * dx = -1 0 +1 and dy = 0 00302 * +1 00303 * 00304 * NAME: Mike Heath 00305 * DATE: 2/15/96 00306 *******************************************************************************/ 00307 void derrivative_x_y(short int *smoothedim, int rows, int cols, 00308 short int **delta_x, short int **delta_y) 00309 { 00310 int r, c, pos; 00311 00312 /**************************************************************************** 00313 * Allocate images to store the derivatives. 00314 ****************************************************************************/ 00315 if(((*delta_x) = (short *) calloc(rows*cols, sizeof(short))) == NULL){ 00316 fprintf(stderr, "Error allocating the delta_x image.\n"); 00317 exit(1); 00318 } 00319 if(((*delta_y) = (short *) calloc(rows*cols, sizeof(short))) == NULL){ 00320 fprintf(stderr, "Error allocating the delta_x image.\n"); 00321 exit(1); 00322 } 00323 00324 /**************************************************************************** 00325 * Compute the x-derivative. Adjust the derivative at the borders to avoid 00326 * losing pixels. 00327 ****************************************************************************/ 00328 for(r=0;r<rows;r++){ 00329 pos = r * cols; 00330 (*delta_x)[pos] = smoothedim[pos+1] - smoothedim[pos]; 00331 pos++; 00332 for(c=1;c<(cols-1);c++,pos++){ 00333 (*delta_x)[pos] = smoothedim[pos+1] - smoothedim[pos-1]; 00334 } 00335 (*delta_x)[pos] = smoothedim[pos] - smoothedim[pos-1]; 00336 } 00337 00338 /**************************************************************************** 00339 * Compute the y-derivative. Adjust the derivative at the borders to avoid 00340 * losing pixels. 00341 ****************************************************************************/ 00342 for(c=0;c<cols;c++){ 00343 pos = c; 00344 (*delta_y)[pos] = smoothedim[pos+cols] - smoothedim[pos]; 00345 pos += cols; 00346 for(r=1;r<(rows-1);r++,pos+=cols){ 00347 (*delta_y)[pos] = smoothedim[pos+cols] - smoothedim[pos-cols]; 00348 } 00349 (*delta_y)[pos] = smoothedim[pos] - smoothedim[pos-cols]; 00350 } 00351 } 00352 00353 /******************************************************************************* 00354 * PROCEDURE: gaussian_smooth 00355 * PURPOSE: Blur an image with a gaussian filter. 00356 * NAME: Mike Heath 00357 * DATE: 2/15/96 00358 *******************************************************************************/ 00359 void gaussian_smooth(unsigned char *image, int rows, int cols, float sigma, 00360 short int **smoothedim) 00361 { 00362 int r, c, rr, cc, /* Counter variables. */ 00363 windowsize, /* Dimension of the gaussian kernel. */ 00364 center; /* Half of the windowsize. */ 00365 float *tempim, /* Buffer for separable filter gaussian smoothing. */ 00366 *kernel, /* A one dimensional gaussian kernel. */ 00367 dot, /* Dot product summing variable. */ 00368 sum; /* Sum of the kernel weights variable. */ 00369 00370 /**************************************************************************** 00371 * Create a 1-dimensional gaussian smoothing kernel. 00372 ****************************************************************************/ 00373 make_gaussian_kernel(sigma, &kernel, &windowsize); 00374 center = windowsize / 2; 00375 00376 /**************************************************************************** 00377 * Allocate a temporary buffer image and the smoothed image. 00378 ****************************************************************************/ 00379 if((tempim = (float *) calloc(rows*cols, sizeof(float))) == NULL){ 00380 fprintf(stderr, "Error allocating the buffer image.\n"); 00381 exit(1); 00382 } 00383 if(((*smoothedim) = (short int *) calloc(rows*cols, 00384 sizeof(short int))) == NULL){ 00385 fprintf(stderr, "Error allocating the smoothed image.\n"); 00386 exit(1); 00387 } 00388 00389 /**************************************************************************** 00390 * Blur in the x - direction. 00391 ****************************************************************************/ 00392 for(r=0;r<rows;r++){ 00393 for(c=0;c<cols;c++){ 00394 dot = 0.0; 00395 sum = 0.0; 00396 for(cc=(-center);cc<=center;cc++){ 00397 if(((c+cc) >= 0) && ((c+cc) < cols)){ 00398 dot += (float)image[r*cols+(c+cc)] * kernel[center+cc]; 00399 sum += kernel[center+cc]; 00400 } 00401 } 00402 tempim[r*cols+c] = dot/sum; 00403 } 00404 } 00405 00406 /**************************************************************************** 00407 * Blur in the y - direction. 00408 ****************************************************************************/ 00409 for(c=0;c<cols;c++){ 00410 for(r=0;r<rows;r++){ 00411 sum = 0.0; 00412 dot = 0.0; 00413 for(rr=(-center);rr<=center;rr++){ 00414 if(((r+rr) >= 0) && ((r+rr) < rows)){ 00415 dot += tempim[(r+rr)*cols+c] * kernel[center+rr]; 00416 sum += kernel[center+rr]; 00417 } 00418 } 00419 (*smoothedim)[r*cols+c] = (short int)(dot*BOOSTBLURFACTOR/sum + 0.5); 00420 } 00421 } 00422 00423 free(tempim); 00424 free(kernel); 00425 } 00426 00427 /******************************************************************************* 00428 * PROCEDURE: make_gaussian_kernel 00429 * PURPOSE: Create a one dimensional gaussian kernel. 00430 * NAME: Mike Heath 00431 * DATE: 2/15/96 00432 *******************************************************************************/ 00433 void make_gaussian_kernel(float sigma, float **kernel, int *windowsize) 00434 { 00435 int i, center; 00436 float x, fx, sum=0.0; 00437 00438 *windowsize = int(1 + 2 * ceil(2.5 * sigma)); 00439 center = (*windowsize) / 2; 00440 00441 if((*kernel = (float *) calloc((*windowsize), sizeof(float))) == NULL){ 00442 fprintf(stderr, "Error callocing the gaussian kernel array.\n"); 00443 exit(1); 00444 } 00445 00446 for(i=0;i<(*windowsize);i++){ 00447 x = (float)(i - center); 00448 fx = pow(2.71828, -0.5*x*x/(sigma*sigma)) / (sigma * sqrt(6.2831853)); 00449 (*kernel)[i] = fx; 00450 sum += fx; 00451 } 00452 00453 for(i=0;i<(*windowsize);i++) (*kernel)[i] /= sum; 00454 00455 } 00456 00457 00458 00459 //finds the line by using hough transform 00460 //thetares is the resolution of each theta 00461 //dRes is the resolution of the D 00462 //returns the number of lines found 00463 std::vector <LineSegment2D> houghTransform(Image<byte> &inputImage, float thetaRes, float dRes, int threshold, Image< PixRGB<byte> > &output) 00464 { 00465 int r; 00466 //get the total number of angles and D from the resolution of theta and D's 00467 int numangle = (int) (PI / thetaRes); //in radians 00468 int numD = (int) (((inputImage.getWidth() + inputImage.getHeight()) * 2 + 1) / dRes); 00469 00470 std::vector <LineSegment2D> lines; 00471 cout << "Performing Hough Line Transform:" << endl; 00472 cout << "numD: " << numD << endl; 00473 cout << "numangle: " << numangle << endl; 00474 00475 //accumulator -> represents the hough space 00476 int accumulator[numD][numangle]; 00477 00478 for(int i = 0; i<numD; i++) 00479 for(int j = 0; j < numangle; j++) 00480 accumulator[i][j] = 0; 00481 00482 //equation of the line is Xcos(theta)+ysin(theta) = D 00483 // fill the accumulator 00484 // printf("numD is %d\n",numD); 00485 for(int j = 0; j < inputImage.getHeight(); j++ ) 00486 for(int i = 0; i < inputImage.getWidth(); i++ ) 00487 { 00488 if( inputImage.getVal(i,j) != 0 )//point is an edge 00489 for(int n = 0; n < numangle; n++ )//get all possible D and theta that can fit to that point 00490 { 00491 r = (int)(i * cos(n*thetaRes) + j * sin(n*thetaRes)); 00492 r += (numD -1)/2; 00493 accumulator[r][n]++; 00494 } 00495 } 00496 00497 Point2D<int> p1; 00498 Point2D<int> p2; 00499 Point2D<int> storage[inputImage.getWidth()*inputImage.getHeight()]; 00500 double a; 00501 int totalLines = 0; 00502 int pointCount = 0; 00503 00504 // find the peaks, ie any number greater than the threshold is considered a line 00505 for(int i = 0; i<numD; i++) 00506 for(int j = 0; j < numangle; j++) 00507 { 00508 if(accumulator[i][j] > threshold && accumulator[i][j]> accumulator[i-1][j] 00509 && accumulator[i][j]> accumulator[i+1][j] && accumulator[i][j]> accumulator[i][j-1] 00510 && accumulator[i][j]> accumulator[i][j+1])//found a peak 00511 { 00512 totalLines++; 00513 00514 //get the image coordinates for the 2 endpoint of the line 00515 a = cos(j*thetaRes); 00516 // b = sin(j*thetaRes); 00517 00518 for(int x = 0; x < inputImage.getWidth(); x++) 00519 for(int y = 0; y < inputImage.getHeight(); y++) 00520 if( inputImage.getVal(x,y) != 0 )//point is an edge 00521 if(((int)(x * cos(j*thetaRes) + y * sin(j*thetaRes))) + ((numD -1)/2) == i) 00522 { 00523 storage[pointCount].i = x; 00524 storage[pointCount].j = y; 00525 pointCount++; 00526 } 00527 00528 //check the distance between each point 00529 bool isolated = true; 00530 int numtoDiscard = 0; 00531 float discardPoints[inputImage.getWidth() * inputImage.getHeight()]; 00532 for(int y = 0; y < pointCount; y++){ 00533 for(int x = 0; x <pointCount; x++){ 00534 if(storage[0].distance(storage[x]) < 1 && x != y) 00535 isolated = false; 00536 00537 } 00538 if(isolated)//discard this point 00539 { 00540 discardPoints[numtoDiscard++] = y; 00541 } 00542 } 00543 00544 00545 00546 //make sure that the point is not in the to be discarded list 00547 for(int x=0; x < numtoDiscard; x++) 00548 for(int y = 0; y < pointCount; y++) 00549 if(discardPoints[x] != y){ 00550 p1.i = p2.i = storage[y].i; 00551 p1.j = p2.j = storage[y].j; 00552 00553 } 00554 //find the 2 endpoints of line 00555 //check if vertical 00556 if(fabs(a) < .001) 00557 { 00558 00559 //search the 2 endpoints 00560 for(int x = 0; x < pointCount; x++) 00561 { 00562 bool discard = false; 00563 for(int k = 0; k < numtoDiscard; k++) 00564 if(discardPoints[k] == x) 00565 discard = true; 00566 if(!discard){ 00567 if(p1.j < storage[x].j) 00568 { 00569 p1.j = storage[x].j; 00570 p1.i = storage[x].i; 00571 } 00572 if(p2.j > storage[x].j) 00573 { 00574 p2.j = storage[x].j; 00575 p2.i = storage[x].i; 00576 00577 } 00578 } 00579 } 00580 00581 } 00582 else // horizontal 00583 { 00584 //search the 2 endpoints 00585 for(int x = 0; x < pointCount; x++) 00586 { 00587 bool discard = false; 00588 for(int k = 0; k < numtoDiscard; k++) 00589 if(discardPoints[k] == x) 00590 discard = true; 00591 if(!discard){ 00592 00593 if(p1.i < storage[x].i) 00594 { 00595 p1.j = storage[x].j; 00596 p1.i = storage[x].i; 00597 } 00598 if(p2.i > storage[x].i) 00599 { 00600 p2.j = storage[x].j; 00601 p2.i = storage[x].i; 00602 00603 } 00604 } 00605 } 00606 } 00607 pointCount = 0; 00608 LineSegment2D thisLine(p1, p2); 00609 lines.push_back(thisLine); 00610 drawLine(output,p1, p2, PixRGB<byte> (255,0,0), 1); 00611 00612 00613 } 00614 } 00615 return lines; 00616 00617 } 00618 00619 /* 00620 functions to find the best fit line 00621 */ 00622 00623 double sigma(double* r, int N) { 00624 double sum=0.0; 00625 for(int i=0;i<N;++i) 00626 sum += r[i]; 00627 return(sum); 00628 } 00629 double sigma(double* r,double* s, int N) { 00630 double sum=0.0; 00631 for(int i=0;i<N;++i) 00632 sum += r[i]*s[i]; 00633 return(sum); 00634 } 00635 void findTanLine(int N, double *x, double *y, float &slope, float &Intercept) { 00636 float delta=N*sigma(x,x, N)-pow(sigma(x,N),2.0); 00637 Intercept = (1.0/delta)*(sigma(x,x,N)*sigma(y,N)-sigma(x,N)*sigma(x,y,N)); 00638 slope = (1.0/delta)*(N*sigma(x,y, N)-sigma(x, N)*sigma(y,N)); 00639 } 00640 00641 00642 /* note that the utility functions below are taken from Robert A. Mclaughlin's code on RHT. 00643 Only slight modification is done to them 00644 */ 00645 00646 00647 /* Find largest (absolute) entry in a 2x2 matrix. 00648 * Return co-ords of entry in (i,j) (row, column) 00649 -Robert A. McLaughlin 00650 */ 00651 void 00652 find_biggest_entry_2x2(double m[][3], int *i, int *j) 00653 { 00654 if ((fabs(m[1][1]) > fabs(m[1][2])) && (fabs(m[1][1]) > fabs(m[2][1])) && (fabs(m[1][1]) > fabs(m[2][2]))) 00655 { 00656 *i = 1; 00657 *j = 1; 00658 } 00659 else if ((fabs(m[1][2]) > fabs(m[2][1])) && (fabs(m[1][2]) > fabs(m[2][2]))) 00660 { 00661 *i = 1; 00662 *j = 2; 00663 } 00664 else if (fabs(m[2][1]) > fabs(m[2][2])) 00665 { 00666 *i = 2; 00667 *j = 1; 00668 } 00669 else 00670 { 00671 *i = 2; 00672 *j = 2; 00673 } 00674 00675 } /* end of find_biggest_entry_2x2() */ 00676 00677 /* 00678 function that finds the eigenvectors 00679 -Robert A. McLaughlin 00680 */ 00681 void 00682 find_eigenvectors_2x2_positive_semi_def_real_matrix(double **covar, double *eig_val, double **eig_vec) 00683 { 00684 double a, b, c, d; 00685 int i, j; 00686 double tmp[3][3]; 00687 00688 /* Find eigenvalues 00689 * Use characteristic equation. 00690 */ 00691 a = 1; 00692 b = -covar[1][1] - covar[2][2]; 00693 c = covar[1][1]*covar[2][2] - covar[1][2]*covar[2][1]; 00694 00695 d = squareOf(b) - 4*a*c; 00696 if (d <= 10e-5) 00697 { 00698 eig_val[1] = -b / 2.0; 00699 eig_val[2] = -b / 2.0; 00700 eig_vec[1][1] = 1.0; eig_vec[1][2] = 0.0; 00701 eig_vec[2][1] = 0.0; eig_vec[2][2] = 1.0; 00702 } 00703 else 00704 { 00705 eig_val[1] = (-b + sqrt(d)) / 2.0; 00706 eig_val[2] = (-b - sqrt(d)) / 2.0; 00707 00708 /* Put eigenvalues in descending order. 00709 */ 00710 if (eig_val[1] < eig_val[2]) 00711 { 00712 d = eig_val[1]; 00713 eig_val[1] = eig_val[2]; 00714 eig_val[2] = d; 00715 } 00716 00717 /* Find first eigenvector. 00718 */ 00719 tmp[1][1] = covar[1][1] - eig_val[1]; 00720 tmp[1][2] = covar[1][2]; 00721 tmp[2][1] = covar[2][1]; 00722 tmp[2][2] = covar[2][2] - eig_val[1]; 00723 00724 00725 find_biggest_entry_2x2(tmp, &i, &j); 00726 if (j == 1) 00727 { 00728 eig_vec[2][1] = 1.0; 00729 eig_vec[1][1] = -tmp[i][2] / tmp[i][1]; 00730 } 00731 else /* j == 2 */ 00732 { 00733 eig_vec[1][1] = 1.0; 00734 eig_vec[2][1] = -tmp[i][1] / tmp[i][2]; 00735 } 00736 /* Normalise eigenvecotr. 00737 */ 00738 d = sqrt(squareOf(eig_vec[1][1]) + squareOf(eig_vec[2][1])); 00739 eig_vec[1][1] /= d; 00740 eig_vec[2][1] /= d; 00741 00742 00743 /* Find secind eigenvector. 00744 */ 00745 eig_vec[1][2] = -eig_vec[2][1]; 00746 eig_vec[2][2] = eig_vec[1][1]; 00747 00748 } 00749 00750 00751 00752 } /* end of find_eigenvectors_2x2_positive_semi_def_real_matrix() */ 00753 00754 00755 void 00756 SwapRows(double *A, double *B, int els) 00757 { 00758 double temp; 00759 int a; 00760 00761 for(a=1;a<=els;a++) 00762 { 00763 temp=A[a]; 00764 A[a]=B[a]; 00765 B[a]=temp; 00766 } 00767 } /* end of SwapRows() */ 00768 00769 00770 void 00771 TakeWeightRow(double *A, double *B, double num, double denom, int els) 00772 { 00773 int a; 00774 00775 for(a=1;a<=els;a++) 00776 A[a]=(A[a]*denom-B[a]*num)/denom; 00777 00778 } /* end of TakeWeightRow() */ 00779 00780 void 00781 ScaleRow(double *A, double fact, int els) 00782 { 00783 int a; 00784 00785 for(a=1;a<=els;a++) 00786 A[a]/=fact; 00787 00788 } /* end of ScaleRow() */ 00789 double ** 00790 alloc_array(int row, int column) 00791 { 00792 double **rowp; 00793 int a; 00794 00795 00796 if ( (rowp=(double **)malloc((unsigned )(row+1) * sizeof(double *)) ) == NULL) 00797 return(NULL); 00798 for(a=1;a<=row;a++) 00799 { 00800 if ((rowp[a]=(double *)malloc((unsigned)(column+1)*sizeof(double))) == NULL) 00801 return(NULL); 00802 } 00803 00804 return(rowp); 00805 00806 } /* end of alloc_array() */ 00807 00808 00809 void 00810 free_array(double **A, int row) 00811 { 00812 int i; 00813 00814 for (i=1; i <= row; i++) 00815 free(A[i]); 00816 free(A); 00817 00818 } /* end of free_array() */ 00819 00820 double *alloc_vector(int dim) 00821 { 00822 return( (double *)malloc(( (unsigned )(dim+1) ) * sizeof(double)) ); 00823 } 00824 00825 /* 00826 finds the inverse of matrix taken from -Robert A. McLaughlin's code 00827 */ 00828 void 00829 find_inverse(double **MAT, double **I, int dim) 00830 { 00831 00832 int a,b,R; 00833 double num,denom; 00834 double **M; 00835 00836 M = alloc_array(dim, dim); 00837 for (a=1; a<=dim; a++) 00838 for (b=1; b<=dim; b++) 00839 M[a][b] = MAT[a][b]; 00840 00841 for(R=1;R<=dim;R++) 00842 for(a=1;a<=dim;a++) 00843 I[R][a]=(R==a)?1:0; 00844 for(R=1;R<=dim;R++) 00845 { 00846 if(M[R][R]==0) 00847 { 00848 for(a=R+1;a<=dim;a++) 00849 if (M[a][R]!=0) break; 00850 if (a==(dim+1)) 00851 { 00852 fprintf(stderr, "Matrix non-invertable, fail on row %d left.\n",R); 00853 fprintf(stderr, "in find_inverse() in lin_algebra.c\n"); 00854 exit(1); 00855 } 00856 SwapRows(M[R],M[a],dim); 00857 SwapRows(I[R],I[a],dim); 00858 } 00859 denom=M[R][R]; 00860 for(a=R+1;a<=dim;a++) 00861 { 00862 num=M[a][R]; 00863 TakeWeightRow(M[a],M[R],num,denom,dim); 00864 TakeWeightRow(I[a],I[R],num,denom,dim); 00865 } 00866 } 00867 for(R=dim;R>=1;R--) 00868 { 00869 for(a=R+1;a<=dim;a++) 00870 { 00871 num=M[R][a]; 00872 denom=M[a][a]; 00873 TakeWeightRow(M[R],M[a],num,denom,dim); 00874 TakeWeightRow(I[R],I[a],num,denom,dim); 00875 } 00876 } 00877 for(R=1;R<=dim;R++) 00878 ScaleRow(I[R],M[R][R],dim); 00879 00880 free_array(M, dim); 00881 00882 00883 } /* end of find_inverse() */ 00884 00885 00886 00887 /* 00888 function that does LU decomposition 00889 taken from Robert A. McLaughlin's code 00890 */ 00891 #define TINY_VALUE 1.0e-20; 00892 int 00893 lu_decomposition(double **M, int n, int *indx, double *d) 00894 { 00895 int i, imax, j, k; 00896 double big,dum,sum,temp; 00897 double *vec; 00898 00899 vec = alloc_vector(n); 00900 *d = 1.0; 00901 for (i=1; i <= n; i++) 00902 { 00903 big=0.0; 00904 for (j=1; j <= n; j++) 00905 if ((temp=fabs(M[i][j])) > big) big=temp; 00906 if (big == 0.0) 00907 { 00908 /* Singular matrix in routine lu_decomposition 00909 */ 00910 return(-1); 00911 } 00912 vec[i]=1.0/big; 00913 } 00914 for (j=1; j <= n; j++) 00915 { 00916 for (i=1; i < j; i++) 00917 { 00918 sum=M[i][j]; 00919 for (k=1;k<i;k++) 00920 sum -= M[i][k]*M[k][j]; 00921 M[i][j]=sum; 00922 } 00923 big=0.0; 00924 imax = 0; 00925 for (i=j; i <= n; i++) 00926 { 00927 sum = M[i][j]; 00928 for (k=1; k < j; k++) 00929 sum -= M[i][k]*M[k][j]; 00930 M[i][j]=sum; 00931 if ( (dum=vec[i]*fabs(sum)) >= big) 00932 { 00933 big = dum; 00934 imax = i; 00935 } 00936 } 00937 if (j != imax) 00938 { 00939 for (k=1; k <= n; k++) 00940 { 00941 dum=M[imax][k]; 00942 M[imax][k]=M[j][k]; 00943 M[j][k]=dum; 00944 } 00945 *d = -(*d); 00946 vec[imax]=vec[j]; 00947 } 00948 indx[j]=imax; 00949 if (M[j][j] == 0.0) 00950 M[j][j] = TINY_VALUE; 00951 if (j != n) 00952 { 00953 dum=1.0/(M[j][j]); 00954 for (i=j+1; i<=n; i++) 00955 M[i][j] *= dum; 00956 } 00957 } 00958 free(vec); 00959 00960 00961 return(0); 00962 00963 } /* end of lu_decomposition() */ 00964 #undef TINY_VALUE 00965 00966 00967 /* 00968 function that does LU back substitution 00969 taken from Robert A. McLaughlin's code 00970 */ 00971 void 00972 lu_back_substitution(double **M, int n, int *indx, double *b) 00973 { 00974 int i, ii=0, ip, j; 00975 double sum; 00976 00977 for (i=1;i<=n;i++) 00978 { 00979 ip=indx[i]; 00980 sum=b[ip]; 00981 b[ip]=b[i]; 00982 if (ii) 00983 for (j=ii;j<=i-1;j++) 00984 sum -= M[i][j]*b[j]; 00985 else if (sum) ii=i; 00986 b[i] = sum; 00987 } 00988 for (i=n; i >=1 ;i--) 00989 { 00990 sum = b[i]; 00991 for (j=i+1;j<=n;j++) 00992 sum -= M[i][j]*b[j]; 00993 b[i] = sum/M[i][i]; 00994 } 00995 } /* end of lu_decomposition() */ 00996 00997 /* function that transforms the a,b,c parameter of the ellipse equation to r1,r2,theta 00998 taken from Robert A. McLaughlin's code 00999 */ 01000 bool 01001 transform_ellipse_parameters(double a, double b, double c, 01002 double *r1, double *r2, double *theta) 01003 { 01004 double **M, **C, **eig_vec; 01005 double *eig_val; 01006 01007 01008 M = alloc_array(2, 2); 01009 C = alloc_array(2, 2); 01010 eig_vec = alloc_array(2, 2); 01011 eig_val = alloc_vector(2); 01012 01013 if ((M == NULL) || (C == NULL) || (eig_val == NULL) || (eig_vec == NULL)) 01014 { 01015 fprintf(stderr, "malloc failed in transform_ellipse_parameters() in rht.c\n"); 01016 exit(1); 01017 } 01018 01019 01020 M[1][1] = a; 01021 M[1][2] = M[2][1] = b; 01022 M[2][2] = c; 01023 find_inverse(M, C, 2); 01024 find_eigenvectors_2x2_positive_semi_def_real_matrix(C, eig_val, eig_vec); 01025 01026 if ((eig_val[1] <= 0) || (eig_val[2] <= 0)) 01027 { 01028 return( false ); 01029 } 01030 01031 01032 *r1 = sqrt(eig_val[1]); 01033 *r2 = sqrt(eig_val[2]); 01034 *theta = atan2(eig_vec[2][1], eig_vec[1][1]); 01035 01036 01037 free_array(M, 2); 01038 free_array(C, 2); 01039 free_array(eig_vec, 2); 01040 free(eig_val); 01041 01042 01043 return( true ); 01044 01045 } /* end of transform_ellipse_parameters() */ 01046 01047 01048 01049 01050 01051 01052 //ellipse parameter node 01053 struct ellipse { 01054 int x, y, confidence; 01055 double r1, r2, theta; 01056 }; 01057 01058 01059 void drawEllipse(Image< PixRGB<byte> > &output, list<ellipse> &houghSpace) 01060 { 01061 01062 list<ellipse>::iterator Iter; 01063 //draw the ellipses 01064 for(Iter = houghSpace.begin(); Iter != houghSpace.end(); Iter++) 01065 { 01066 //cout<<"drawing "<<endl; 01067 //drawCircle(output,Point2D<int>((*Iter).x,(*Iter).y),(int)(*Iter).c,PixRGB<byte> (255,0,0)); 01068 // for(int u = 0; u < inputImage.getWidth(); u++) 01069 //for(int v = 0; v < inputImage.getHeight(); v++) 01070 // if(((int)((*Iter).a * pow((float)(u - (*Iter).x),2)) + (2 * (*Iter).b * (u - (*Iter).x) * (v- (*Iter).y)) + ((*Iter).c * pow((float)(v-(*Iter).y),2)))== 1 && (*Iter).confidence > threshold){ 01071 // drawPoint(output,u,v,PixRGB<byte> (255,0,0)); 01072 // cout<<"drawing confidence is "<< (*Iter).confidence<<endl; 01073 // } 01074 01075 int width = 20; 01076 int x, y; 01077 int x1, y1, x2, y2; 01078 double perimeter, incr, grad, rho; 01079 double M[3][3]; 01080 int offset; 01081 bool error = false; 01082 01083 if (((*Iter).r1 <= 20) || ((*Iter).r2 <= 20)) 01084 error = true; 01085 01086 01087 /* The ellipse will be contructed as a sequence of 01088 * line segments. 01089 * incr indicates how much of the ellipse 01090 * each line segments will cover. 01091 */ 01092 perimeter = 3.14159 * ( 3*(fabs((*Iter).r1)+fabs((*Iter).r2)) - sqrt( (fabs((*Iter).r1)+3*fabs((*Iter).r2))*(3*fabs((*Iter).r1)+fabs((*Iter).r2)) ) ); 01093 // cout<<"p is "<<(*Iter).x<<"q is "<<(*Iter).y<<"r1 is "<<(*Iter).r1<<"r2 is "<<(*Iter).r2<<"theta is "<<(*Iter).theta<<endl; 01094 // cout<<"confidence is "<<(*Iter).confidence<<endl; 01095 /* Error check. 01096 */ 01097 //cout<<"perimeter is "<<perimeter<<endl; 01098 if (perimeter <= 20) 01099 error = true; 01100 01101 01102 if(!error && (*Iter).confidence > 10) 01103 { 01104 // cout<<"!error"<<endl; 01105 incr = 60.0/perimeter; 01106 01107 01108 /* The ellipse is defined as all points [x,y] given by: 01109 * 01110 * { [x,y] : for all theta in [0, 2pi), 01111 * [x,y] = E . D . E^-1 . [cos(theta), sin (theta)} 01112 * 01113 * where E is the matrix containing the direction of 01114 * the pricipal axes 01115 * of the ellipse 01116 * and D is: - - 01117 * |r1 0 | 01118 * | 0 r2| 01119 * - - 01120 * First calculate E . D . E^-1 01121 */ 01122 M[1][1] = (*Iter).r1*pow(cos((*Iter).theta),2) + (*Iter).r2*pow(sin((*Iter).theta),2); 01123 M[1][2] = M[2][1] = ((*Iter).r1-(*Iter).r2)*sin((*Iter).theta)*cos((*Iter).theta); 01124 M[2][2] = (*Iter).r1*(pow(sin((*Iter).theta),2)) + (*Iter).r2*(pow(cos((*Iter).theta),2)); 01125 01126 x1 = (int )((*Iter).x + M[1][1]*cos(0.0) + M[1][2]*sin(0.0)); 01127 y1 = (int )((*Iter).y + M[2][1]*cos(0.0) + M[2][2]*sin(0.0)); 01128 01129 01130 for (rho=incr; rho < (2*M_PI + incr); rho += incr) 01131 { 01132 x2 = (int )((*Iter).x + M[1][1]*cos(rho) + M[1][2]*sin(rho)); 01133 y2 = (int )((*Iter).y + M[2][1]*cos(rho) + M[2][2]*sin(rho)); 01134 if ((x1 >= 0) && (x1 < output.getWidth()) && (y1 >= 0) && (y1 < output.getHeight()) && 01135 (x2 >= 0) && (x2 < output.getWidth()) && (y2 >= 0) && (y2 < output.getHeight())) 01136 { 01137 if (fabs(x2-x1) >= fabs(y2-y1)) 01138 { 01139 /* x changes more than y 01140 */ 01141 if (x1 < x2) 01142 { 01143 grad = ((double )(y2-y1)) / (double )(x2-x1); 01144 for (x=x1; x <= x2; x++) 01145 { 01146 y = y1 + (int )(grad*(double )(x - x1)); 01147 for (offset=-width/2; offset < (width+1)/2; offset++) 01148 { 01149 if (((y+offset) >= 0) && ((y+offset) < output.getHeight())) 01150 { 01151 drawPoint(output,x,y/*+offset*/,PixRGB<byte> (255,0,0)); 01152 // cout<<"DRAWING POINT"<<endl; 01153 } 01154 } 01155 } 01156 } 01157 else if (x1 > x2) 01158 { 01159 grad = ((double )(y1-y2)) / (double )(x1-x2); 01160 for (x=x2; x <= x1; x++) 01161 { 01162 y = y2 + (int )(grad*(double )(x - x2)); 01163 for (offset=-width/2; offset < (width+1)/2; offset++) 01164 { 01165 if (((y+offset) >= 0) && ((y+offset) < output.getHeight())) 01166 { 01167 drawPoint(output,x,y/*+offset*/,PixRGB<byte> (255,0,0)); 01168 // cout<<"DRAWING POINT"<<endl; 01169 } 01170 } 01171 } 01172 } 01173 } 01174 else /* if (abs(x2-x1) < abs(y2-y1)) */ 01175 { 01176 /* y changes more than x 01177 */ 01178 if (y1 < y2) 01179 { 01180 grad = ((double )(x2-x1)) / (double )(y2-y1); 01181 for (y=y1; y <= y2; y++) 01182 { 01183 x = x1 + (int )(grad*(double )(y-y1)); 01184 for (offset=-width/2; offset < (width+1)/2; offset++) 01185 { 01186 if (((x+offset) >= 0) && ((x+offset) < output.getWidth())) 01187 { 01188 drawPoint(output,x/*+offset*/,y,PixRGB<byte> (255,0,0)); 01189 //cout<<"DRAWING POINT"<<endl; 01190 } 01191 } 01192 } 01193 } 01194 else if (y1 > y2) 01195 { 01196 grad = ((double )(x1-x2)) / (double )(y1-y2); 01197 for (y=y2; y <= y1; y++) 01198 { 01199 x = x2 + (int )(grad*(double )(y - y2)); 01200 for (offset=-width/2; offset < (width+1)/2; offset++) 01201 { 01202 if (((x+offset) >= 0) && ((x+offset) < output.getWidth())) 01203 { 01204 drawPoint(output,x/*+offset*/,y,PixRGB<byte> (255,0,0)); 01205 // cout<<"DRAWING POINT"<<endl; 01206 } 01207 } 01208 } 01209 } 01210 } 01211 } /* end of 'if ((x1 >= 0) && (x1 < aImage->x) &&...' */ 01212 x1 = x2; 01213 y1 = y2; 01214 } 01215 } 01216 } 01217 01218 } 01219 01220 01221 int 01222 count_pixels(Image<byte> &output, double x_centre, double y_centre, 01223 double r1, double r2, double theta) 01224 { 01225 /* 01226 * x, y; -- centre of ellipse 01227 * r1, r2; -- major/minor axis 01228 * theta; -- angle 01229 */ 01230 int width = 20; 01231 int x, y; 01232 int x1, y1, x2, y2; 01233 double perimeter, incr, grad, rho; 01234 double M[3][3]; 01235 int offset; 01236 int num_pixels = 0; 01237 01238 if ((r1 <= width) || (r2 <= width)) 01239 return(0); 01240 01241 01242 01243 /* The ellipse will be contructed as a sequence of 01244 * line segments. 01245 * incr indicates how much of the ellipse 01246 * each line segments will cover. 01247 */ 01248 perimeter = M_PI * ( 3*(fabs(r1)+fabs(r2)) - sqrt( (fabs(r1)+3*fabs(r2))*(3*fabs(r1)+fabs(r2)) ) ); 01249 01250 01251 /* Error check. 01252 */ 01253 if (perimeter <= width) 01254 return( 0 ); /* zero width or length ellipse. 01255 * Don't bother counting pixels. 01256 */ 01257 01258 01259 01260 incr = 60.0/perimeter; 01261 01262 01263 /* The ellipse is defined as all points [x,y] given by: 01264 * 01265 * { [x,y] : for all theta in [0, 2pi), 01266 * [x,y] = E . D . E^-1 . [cos(theta), sin (theta)} 01267 * 01268 * where E is the matrix containing the direction of 01269 * the pricipal axes 01270 * of the ellipse 01271 * and D is: - - 01272 * |r1 0 | 01273 * | 0 r2| 01274 * - - 01275 * First calculate E . D . E^-1 01276 */ 01277 M[1][1] = r1*squareOf(cos(theta)) + r2*squareOf(sin(theta)); 01278 M[1][2] = M[2][1] = (r1-r2)*sin(theta)*cos(theta); 01279 M[2][2] = r1*squareOf(sin(theta)) + r2*squareOf(cos(theta)); 01280 01281 x1 = (int )(x_centre + M[1][1]*cos(0.0) + M[1][2]*sin(0.0)); 01282 y1 = (int )(y_centre + M[2][1]*cos(0.0) + M[2][2]*sin(0.0)); 01283 01284 01285 for (rho=incr; rho < (2*M_PI + incr); rho += incr) 01286 { 01287 x2 = (int )(x_centre + M[1][1]*cos(rho) + M[1][2]*sin(rho)); 01288 y2 = (int )(y_centre + M[2][1]*cos(rho) + M[2][2]*sin(rho)); 01289 if ((x1 >= 0) && (x1 < output.getWidth()) && (y1 >= 0) && (y1 < output.getHeight()) && 01290 (x2 >= 0) && (x2 < output.getWidth()) && (y2 >= 0) && (y2 < output.getHeight())) 01291 { 01292 if (abs(x2-x1) >= abs(y2-y1)) 01293 { 01294 /* x changes more than y 01295 */ 01296 if (x1 < x2) 01297 { 01298 grad = ((double )(y2-y1)) / (double )(x2-x1); 01299 for (x=x1; x <= x2; x++) 01300 { 01301 y = y1 + (int )(grad*(double )(x - x1)); 01302 for (offset=-width/2; offset < (width+1)/2; offset++) 01303 { 01304 if (((y+offset) >= 0) && ((y+offset) < output.getHeight())) 01305 { 01306 if ((output.getVal(x,y+offset) == 255)) 01307 { 01308 num_pixels++; 01309 } 01310 } 01311 } 01312 } 01313 } 01314 else if (x1 > x2) 01315 { 01316 grad = ((double )(y1-y2)) / (double )(x1-x2); 01317 for (x=x2; x <= x1; x++) 01318 { 01319 y = y2 + (int )(grad*(double )(x - x2)); 01320 for (offset=-width/2; offset < (width+1)/2; offset++) 01321 { 01322 if (((y+offset) >= 0) && ((y+offset) < output.getHeight())) 01323 { 01324 if ((output.getVal(x,y+offset) == 255)) 01325 { 01326 num_pixels++; 01327 } 01328 } 01329 } 01330 } 01331 } 01332 } 01333 else /* if (abs(x2-x1) < abs(y2-y1)) */ 01334 { 01335 /* y changes more than x 01336 */ 01337 if (y1 < y2) 01338 { 01339 grad = ((double )(x2-x1)) / (double )(y2-y1); 01340 for (y=y1; y <= y2; y++) 01341 { 01342 x = x1 + (int )(grad*(double )(y-y1)); 01343 for (offset=-width/2; offset < (width+1)/2; offset++) 01344 { 01345 if (((x+offset) >= 0) && ((x+offset) < output.getWidth())) 01346 { 01347 if ((output.getVal(x+offset,y) == 255)) 01348 { 01349 num_pixels++; 01350 } 01351 } 01352 } 01353 } 01354 } 01355 else if (y1 > y2) 01356 { 01357 grad = ((double )(x1-x2)) / (double )(y1-y2); 01358 for (y=y2; y <= y1; y++) 01359 { 01360 x = x2 + (int )(grad*(double )(y - y2)); 01361 for (offset=-width/2; offset < (width+1)/2; offset++) 01362 { 01363 if (((x+offset) >= 0) && ((x+offset) < output.getWidth())) 01364 { 01365 if ((output.getVal(x+offset,y) == 255)) 01366 { 01367 num_pixels++; 01368 } 01369 } 01370 } 01371 } 01372 } 01373 } 01374 } /* end of 'if ((x1 >= 0) && (x1 < aImage->x) &&...' */ 01375 x1 = x2; 01376 y1 = y2; 01377 } 01378 01379 01380 return( num_pixels ); 01381 01382 01383 } /* count_number_of_pixels_near_ellipse() */ 01384 01385 //Finds Ellipses in an image 01386 //going to use the randomized hough transform to do this 01387 void houghEllipse(Image<byte> inputImage, int threshold, Image< PixRGB<byte> > &output) { 01388 01389 Point2D<int> randomPixels[3]; 01390 double pointX[3][25];//the neighborhood pixels of the 3 chosen random "edge"pixels 01391 double pointY[3][25]; 01392 float slopeTan[3]; 01393 float InterTan[3]; 01394 float slopeCenterLine[2] = { 0.0F, 0.0F }; 01395 float InterCenterLine[2] = { 0.0F, 0.0F }; 01396 01397 bool foundCenterPoint = true; 01398 bool foundOtherParams = true; 01399 01400 int p, q; 01401 double a,b,c,r1,r2,theta; 01402 double **matrixA; 01403 double *vectorB; 01404 double d; 01405 int* indx= (int *)malloc(4 * sizeof(int) ); 01406 01407 matrixA = alloc_array(3,3); 01408 vectorB = alloc_vector(3); 01409 01410 list<ellipse> houghSpace; 01411 list<ellipse>::iterator Iter; 01412 int NUMBER=0; 01413 int Iterations = 0; 01414 01415 int max = 0; 01416 list<ellipse>::iterator indexOfMax; 01417 list<ellipse> tempSpace; 01418 01419 srand( time(NULL)); 01420 while(Iterations < inputImage.getWidth() * inputImage.getHeight())// && NUMBER < 6000) 01421 { 01422 01423 // cout<<"iterations"<<Iterations<<endl; 01424 int pixelCount = 0; 01425 while(pixelCount != 3)//get the initial random numbers 01426 { 01427 randomPixels[pixelCount].i = rand()%320; 01428 randomPixels[pixelCount].j = rand()%240; 01429 if(inputImage.getVal(randomPixels[pixelCount].i, randomPixels[pixelCount].j) == 255) 01430 { 01431 if(pixelCount == 1) 01432 { 01433 if(!(randomPixels[pixelCount].i == randomPixels[0].i)) 01434 { 01435 pixelCount++; 01436 //inputImage.setVal(randomPixels[pixelCount-1].i,randomPixels[pixelCount-1].j,0); 01437 // cout<<"chosen points"<<randomPixels[pixelCount-1].i<<" "<<randomPixels[pixelCount-1].j<<endl; 01438 } 01439 01440 }else if(pixelCount == 2) 01441 { 01442 if(!(randomPixels[pixelCount].i == randomPixels[0].i || randomPixels[pixelCount].i == randomPixels[1].i)) 01443 { 01444 pixelCount++; 01445 //inputImage.setVal(randomPixels[pixelCount-1].i,randomPixels[pixelCount-1].j,0); 01446 //cout<<"chosen points"<<randomPixels[pixelCount-1].i<<" "<<randomPixels[pixelCount-1].j<<endl; 01447 } 01448 } 01449 else 01450 { 01451 pixelCount++; 01452 //inputImage.setVal(randomPixels[pixelCount-1].i,randomPixels[pixelCount-1].j,0); 01453 // cout<<"chosen points"<<randomPixels[pixelCount-1].i<<" "<<randomPixels[pixelCount-1].j<<endl; 01454 } 01455 01456 } 01457 // NUMBER++; 01458 // if(NUMBER == 1000) 01459 // break; 01460 } 01461 // if(NUMBER == 1000) 01462 // break; 01463 01464 foundCenterPoint = true; 01465 foundOtherParams = true; 01466 01467 //cout<<"after getting random pixels"<<endl; 01468 //find the tangent lines 01469 //get the neighboring pixels of each pixel 01470 int count[3] = {0,0,0}; 01471 int tempI = 0, tempJ = 0; 01472 for(int j = 0; j < 3; j++) 01473 { 01474 for(int i = 0; i < 5; i++) 01475 for(int k = 0; k < 5; k++) 01476 { 01477 tempI = randomPixels[j].i + (i-2); 01478 tempJ = randomPixels[j].j + (k-2); 01479 if(tempI >=0 && tempI <320 && tempJ >= 0 && tempJ < 240){ 01480 if(inputImage.getVal(tempI,tempJ)==255 || (tempI == randomPixels[j].i && tempJ == randomPixels[j].j)) 01481 { 01482 if(randomPixels[j].j != tempJ || (randomPixels[j].j == tempJ && randomPixels[j].i == tempI)){ 01483 pointX[j][count[j]] = tempI; 01484 pointY[j][count[j]] = tempJ; 01485 count[j]++; 01486 } 01487 } 01488 } 01489 01490 } 01491 01492 } 01493 // cout<<"test2"<<endl; 01494 // solve for the slope of the tangent 01495 for(int i = 0; i < 3; i++){ 01496 findTanLine(count[i], pointX[i], pointY[i], slopeTan[i], InterTan[i]); 01497 //cout<<"slopeTan is "<<slopeTan[i]<<endl; 01498 } 01499 01500 //solve for the ellipse center 01501 for(int i = 0; i < 2; i++){ 01502 if(!isnan(slopeTan[i]) && !isnan(slopeTan[i+1])) 01503 { 01504 slopeCenterLine[i] = ((slopeTan[i] *((InterTan[i+1]-InterTan[i])/(slopeTan[i]-slopeTan[i+1]))) + InterTan[i] - ((randomPixels[i].j+randomPixels[i+1].j)/2.0))/(((InterTan[i+1]-InterTan[i])/(slopeTan[i]-slopeTan[i+1])) - ((randomPixels[i].i+randomPixels[i+1].i)/2.0)); 01505 InterCenterLine[i] = ((randomPixels[i].j + randomPixels[i+1].j)/2.0) - slopeCenterLine[i] * ((randomPixels[i].i + randomPixels[i+1].i)/2.0); 01506 if(isnan(slopeCenterLine[i])) 01507 { 01508 InterCenterLine[i] = (randomPixels[i].i + randomPixels[i+1].i)/2.0; 01509 } 01510 } 01511 else if(!isnan(slopeTan[i]) && isnan(slopeTan[i+1])) //one is vertical 01512 { 01513 slopeCenterLine[i] = ((slopeTan[i] * randomPixels[i+1].i + InterTan[i]) - ((randomPixels[i].j + randomPixels[i+1].j)/2.0)) / (randomPixels[i+1].i - ((randomPixels[i].i + randomPixels[i+1].i)/2.0)); 01514 InterCenterLine[i] = ((randomPixels[i].j + randomPixels[i+1].j)/2.0) - slopeCenterLine[i] * ((randomPixels[i].i + randomPixels[i+1].i)/2.0); 01515 if(isnan(slopeCenterLine[i])) 01516 { 01517 InterCenterLine[i] = randomPixels[i].i; //store the x-intercept instead 01518 } 01519 } 01520 else if(!isnan(slopeTan[i+1]) && isnan(slopeTan[i]))//another one is vertical 01521 { 01522 slopeCenterLine[i] = ((slopeTan[i+1] * randomPixels[i].i + InterTan[i+1]) - ((randomPixels[i].j + randomPixels[i+1].j)/2.0)) / (randomPixels[i+1].i - ((randomPixels[i].i + randomPixels[i+1].i)/2.0)); 01523 InterCenterLine[i] = ((randomPixels[i].j + randomPixels[i+1].j)/2.0) - slopeCenterLine[i] * ((randomPixels[i].i + randomPixels[i+1].i)/2.0); 01524 if(isnan(slopeCenterLine[i])) 01525 { 01526 InterCenterLine[i] = ((randomPixels[i].i + randomPixels[i+1].i)/2.0); 01527 } 01528 } 01529 else //both are vertical lines? 01530 { 01531 foundCenterPoint = false; 01532 } 01533 01534 } 01535 if(foundCenterPoint) 01536 { 01537 // cout<<"slope center line is "<<slopeCenterLine[0]<<" "<<slopeCenterLine[1]<<endl; 01538 if(!isnan(slopeCenterLine[0]) && !isnan(slopeCenterLine[1])) 01539 { 01540 //The intersection of the 2 lines above is the center 01541 p = (int)((InterCenterLine[1]-InterCenterLine[0])/(slopeCenterLine[0]-slopeCenterLine[1])); 01542 q = (int)((slopeCenterLine[0]*p)+InterCenterLine[0]); 01543 } 01544 else if(!isnan(slopeCenterLine[0]) && isnan(slopeCenterLine[1]) ) // one of the slope is vertical 01545 { 01546 p = (int) InterCenterLine[1]; 01547 q = (int) ((slopeCenterLine[0] * InterCenterLine[1]) + InterCenterLine[0]); 01548 } 01549 else if(!isnan(slopeCenterLine[1]) && isnan(slopeCenterLine[0]) )//same as above 01550 { 01551 p = (int) InterCenterLine[0]; 01552 q = (int) ((slopeCenterLine[1] * InterCenterLine[0]) + InterCenterLine[1]); 01553 } 01554 else 01555 { 01556 p = -1; 01557 q = -1; 01558 //both lines are vertical what to do? 01559 foundCenterPoint = false; 01560 01561 } 01562 if(p < 0 || p > 320 || q < 0 || q > 240) 01563 foundCenterPoint = false; 01564 } 01565 01566 if(foundCenterPoint) // continue only if successfully found the center point of the ellipse 01567 { 01568 for(int i = 1; i < 4; i++) 01569 { 01570 // cout<<"point "<<i<<" is ("<<randomPixels[i].i<<","<<randomPixels[i].j<<")"<<endl; 01571 matrixA[i][1] = (randomPixels[i-1].i - p) * (randomPixels[i-1].i- p); 01572 matrixA[i][2] =(randomPixels[i-1].i-p) * (randomPixels[i-1].j-q) * 2; 01573 matrixA[i][3] = (randomPixels[i-1].j-q) * (randomPixels[i-1].j-q); 01574 vectorB[i] = 1; 01575 } 01576 01577 01578 if (lu_decomposition(matrixA, 3, indx, &d) != 0) 01579 { 01580 foundOtherParams = false; 01581 } 01582 if(foundOtherParams) 01583 { 01584 lu_back_substitution(matrixA, 3, indx,vectorB); 01585 01586 a = vectorB[1]; 01587 b = vectorB[2]; 01588 c = vectorB[3]; 01589 //cout << "a is "<<a<<" b is "<<b<<" c is "<<c<<" sqrt(a^2+b^2)"<<sqrt(a*a+b*b)<<endl; 01590 if(a * c - b * b > 0) 01591 { 01592 if(transform_ellipse_parameters(a, b, c,&r1, &r2, &theta)) 01593 { 01594 // double pixcnt = ((double)count_pixels(inputImage,p,q,r1,r2,theta))/( 3.14159 * ( 3*(r1+r2) - sqrt((r1+3*r2)*(3*r1+r2)) )); 01595 // cout<<pixcnt<<endl; 01596 //if(pixcnt > .5) 01597 if(true) 01598 { 01599 //cout<<" p is "<<p<<" q is "<<q<<endl; 01600 //cout<<"found ellipse number so far is "<<houghSpace.size()<<" NUMBER IS"<<NUMBER<<endl; 01601 bool found = false; 01602 //ellipse 01603 for(Iter = houghSpace.begin(); Iter != houghSpace.end(); Iter++) 01604 { 01605 if(( fabs((*Iter).x - p) < 10 && fabs((*Iter).y - q) < 10 && fabs((*Iter).r1 - r1) < 5 && fabs((*Iter).r2 - r2) < 5 && fabs((*Iter).theta - theta) < .4487)) 01606 { 01607 found = true; 01608 (*Iter).confidence++; 01609 if((*Iter).confidence > max) 01610 { 01611 max = (*Iter).confidence; 01612 indexOfMax = Iter; 01613 } 01614 } 01615 } 01616 if(!found) 01617 { 01618 ellipse temp; 01619 temp.x = p; 01620 temp.y = q; 01621 temp.r1 = r1; 01622 temp.r2 = r2; 01623 temp.theta = theta; 01624 temp.confidence = 1; 01625 houghSpace.push_back(temp); 01626 } 01627 NUMBER++; 01628 } 01629 } 01630 } 01631 } 01632 } 01633 Iterations++; 01634 01635 } 01636 // cout << ((double)count_pixels(inputImage,(*indexOfMax).x,(*indexOfMax).y,(*indexOfMax).r1,(*indexOfMax).r2,(*indexOfMax).theta))/(3* 3.14159 * ( 3*((*indexOfMax).r1+(*indexOfMax).r2) - sqrt(((*indexOfMax).r1+3*(*indexOfMax).r2)*(3*(*indexOfMax).r1+(*indexOfMax).r2)) ))<<endl; 01637 ellipse temp; 01638 temp.x = (*indexOfMax).x; 01639 temp.y = (*indexOfMax).y; 01640 temp.r1 = (*indexOfMax).r1; 01641 temp.r2 = (*indexOfMax).r2; 01642 temp.theta = (*indexOfMax).theta; 01643 temp.confidence = (*indexOfMax).confidence; 01644 cout<<temp.confidence<<endl; 01645 tempSpace.push_back(temp); 01646 drawEllipse(output, tempSpace); 01647 free(indx); 01648 01649 } 01650 01651 01652 01653 01654 01655 01656 01657 01658 int main() { 01659 float sigma = .7; 01660 float tlow = 0.2; 01661 float thigh = .97; 01662 01663 unsigned char *edge; 01664 01665 char *dirfilename = NULL; 01666 01667 01668 ModelManager camManager("ColorTracker Tester"); 01669 nub::soft_ref<FrameIstream> 01670 gb(makeIEEE1394grabber(camManager, "COcam", "cocam")); 01671 01672 camManager.addSubComponent(gb); 01673 01674 camManager.loadConfig("camconfig.pmap"); 01675 01676 gb->setModelParamVal("FrameGrabberSubChan", 0); 01677 gb->setModelParamVal("FrameGrabberBrightness", 128); 01678 gb->setModelParamVal("FrameGrabberHue", 180); 01679 01680 01681 01682 camManager.start(); 01683 01684 Image< PixRGB<byte> > cameraImage; 01685 Image<byte> grayImg; 01686 Image< PixRGB<byte> > houghImg(320,240,ZEROS) ; 01687 //Image<byte> houghImg; 01688 cameraImage = gb->readRGB(); 01689 xwin = new XWindow(cameraImage.getDims()); 01690 xwin2 = new XWindow(cameraImage.getDims()); 01691 xwin3 = new XWindow(cameraImage.getDims()); 01692 01693 while(1) { 01694 01695 printf("."); 01696 cameraImage = gb->readRGB(); 01697 01698 xwin->drawImage(cameraImage); 01699 grayImg = luminance(cameraImage); 01700 01701 canny(grayImg.getArrayPtr(), grayImg.getHeight(), grayImg.getWidth(), sigma, tlow, thigh, &edge, dirfilename); 01702 Image<unsigned char> edgeImage(edge, grayImg.getWidth(), grayImg.getHeight()); 01703 xwin2->drawImage(edgeImage); 01704 houghImg = edgeImage; 01705 01706 std::vector <LineSegment2D> lines = houghTransform(edgeImage, PI/180, 1, 80, houghImg); 01707 //houghEllipse(edgeImage,0,houghImg); 01708 xwin3->drawImage(houghImg); 01709 01710 01711 } 01712 01713 01714 } 01715 01716 // ###################################################################### 01717 /* So things look consistent in everyone's emacs... */ 01718 /* Local Variables: */ 01719 /* mode: c++ */ 01720 /* indent-tabs-mode: nil */ 01721 /* End: */ 01722 01723 #endif // BEOSUB_TEST_HOUGH_C_DEFINED