00001 /*! @file BeoSub/test-canny.C [put description here] */ 00002 00003 /******************************************************************************* 00004 * -------------------------------------------- 00005 *(c) 2001 University of South Florida, Tampa 00006 * Use, or copying without permission prohibited. 00007 * PERMISSION TO USE 00008 * In transmitting this software, permission to use for research and 00009 * educational purposes is hereby granted. This software may be copied for 00010 * archival and backup purposes only. This software may not be transmitted 00011 * to a third party without prior permission of the copyright holder. This 00012 * permission may be granted only by Mike Heath or Prof. Sudeep Sarkar of 00013 * University of South Florida (sarkar@csee.usf.edu). Acknowledgment as 00014 * appropriate is respectfully requested. 00015 * 00016 * Heath, M., Sarkar, S., Sanocki, T., and Bowyer, K. Comparison of edge 00017 * detectors: a methodology and initial study, Computer Vision and Image 00018 * Understanding 69 (1), 38-54, January 1998. 00019 * Heath, M., Sarkar, S., Sanocki, T. and Bowyer, K.W. A Robust Visual 00020 * Method for Assessing the Relative Performance of Edge Detection 00021 * Algorithms, IEEE Transactions on Pattern Analysis and Machine 00022 * Intelligence 19 (12), 1338-1359, December 1997. 00023 * ------------------------------------------------------ 00024 * 00025 * PROGRAM: canny_edge 00026 * PURPOSE: This program implements a "Canny" edge detector. The processing 00027 * steps are as follows: 00028 * 00029 * 1) Convolve the image with a separable gaussian filter. 00030 * 2) Take the dx and dy the first derivatives using [-1,0,1] and [1,0,-1]'. 00031 * 3) Compute the magnitude: sqrt(dx*dx+dy*dy). 00032 * 4) Perform non-maximal suppression. 00033 * 5) Perform hysteresis. 00034 * 00035 * The user must input three parameters. These are as follows: 00036 * 00037 * sigma = The standard deviation of the gaussian smoothing filter. 00038 * tlow = Specifies the low value to use in hysteresis. This is a 00039 * fraction (0-1) of the computed high threshold edge strength value. 00040 * thigh = Specifies the high value to use in hysteresis. This fraction (0-1) 00041 * specifies the percentage point in a histogram of the gradient of 00042 * the magnitude. Magnitude values of zero are not counted in the 00043 * histogram. 00044 * 00045 * NAME: Mike Heath 00046 * Computer Vision Laboratory 00047 * University of South Floeida 00048 * heath@csee.usf.edu 00049 * 00050 * DATE: 2/15/96 00051 * 00052 * Modified: 5/17/96 - To write out a floating point RAW headerless file of 00053 * the edge gradient "up the edge" where the angle is 00054 * defined in radians counterclockwise from the x direction. 00055 * (Mike Heath) 00056 *******************************************************************************/ 00057 00058 // 00059 // $HeadURL: svn://isvn.usc.edu/software/invt/trunk/saliency/src/BeoSub/test-canny.C $ 00060 // $Id: test-canny.C 9412 2008-03-10 23:10:15Z farhan $ 00061 // Primary maintainer for this file: Zack Gossman <gossman@usc.edu> 00062 // 00063 00064 00065 /**************************TODO************************* 00066 -NOTE: switching to std vecotrs BREAKS powell, even if changed throughout. Note sure why 00067 00068 -MAKE a tester execuatable to spool camera values (similar to those displayed at the start of test-multigrab) continually, checking white balance, exposure and gain in order to manually clibrate the cameras at wet test time. Auto set is not working well. NOTE that every color setting is currently rcognizing white! this is bad! something wrong with the V range? <-seems fixable using a mix of changing V and hardware stuff 00069 00070 -SUGGESTION: instead of returning final recognized position data using a struct, inseat store data in shape class and return using reference. (allows shapes with different dimensions to return useful data, unlike current standard 5 dims) 00071 *******************************************************/ 00072 00073 #include "BeoSub/CannyModel.H" 00074 #include "BeoSub/hysteresis.H" 00075 #include "Component/ModelManager.H" 00076 #include "Devices/FrameGrabberFactory.H" 00077 #include "GUI/XWindow.H" 00078 #include "Image/ColorOps.H" 00079 #include "Image/DrawOps.H" 00080 #include "Image/FilterOps.H" 00081 #include "Image/Image.H" 00082 #include "Image/MathOps.H" 00083 #include "Image/MorphOps.H" // for erodeImg(), closeImg() 00084 #include "Image/Transforms.H" 00085 #include "Raster/Raster.H" 00086 #include "Util/MathFunctions.H" // for squareOf() 00087 #include "Util/Timer.H" 00088 #include "Util/Types.H" 00089 #include "Util/log.H" 00090 #include "VFAT/segmentImageTrackMC.H" 00091 #include "rutz/shared_ptr.h" 00092 00093 #include <algorithm> // for std::max() 00094 #include <cmath> 00095 #include <cstdio> 00096 #include <cstdlib> 00097 #include <cstring> 00098 00099 //canny 00100 #define BOOSTBLURFACTOR 90.0 00101 //powell 00102 #define TOL 2.0e-4 00103 #define ITMAX 200 00104 //brent... 00105 #define ITMAXB 100 00106 #define CGOLD 0.3819660 00107 #define ZEPS 1.0e-10 00108 #define SHFT(a,b,c,d) (a)=(b);(b)=(c);(c)=(d); 00109 #define SIGN(a,b) ((b)>=0.0?fabs(a):-fabs(a)) 00110 //mnbrak 00111 #define GOLD 1.618034 00112 #define GLIMIT 100.0 00113 #define TINY 1.0e-20 00114 00115 //CAMERA STUFF 00116 00117 00118 #define FREE_ARG char* 00119 //END CAMERA STUFF 00120 00121 00122 int canny(unsigned char *image, int rows, int cols, float sigma, 00123 float tlow, float thigh, unsigned char **edge, const char* fname); 00124 void gaussian_smooth(unsigned char *image, int rows, int cols, float sigma, 00125 short int **smoothedim); 00126 void make_gaussian_kernel(float sigma, float **kernel, int *windowsize); 00127 void derrivative_x_y(short int *smoothedim, int rows, int cols, 00128 short int **delta_x, short int **delta_y); 00129 void magnitude_x_y(short int *delta_x, short int *delta_y, int rows, int cols, 00130 short int **magnitude); 00131 void radian_direction(short int *delta_x, short int *delta_y, int rows, 00132 int cols, float **dir_radians, int xdirtag, int ydirtag); 00133 void radian_direction(short int *delta_x, short int *delta_y, int rows, 00134 int cols, float **dir_radians, int xdirtag, int ydirtag); 00135 double angle_radians(double x, double y); 00136 void grabImage(Image<PixRGB<byte> >* image); 00137 00138 Image<float> distMap; 00139 rutz::shared_ptr<XWindow> xwin, win; 00140 const bool debugmode = true; 00141 00142 //Moved to global 00143 00144 bool fromFile = true;//boolean to check if input is from file or from camera 00145 00146 const char *infilename = NULL; /* Name of the input image */ 00147 const char *dirfilename = NULL; /* Name of the output gradient direction image */ 00148 const char *shapeArg = NULL; /* Shape for recognition*/ 00149 const char *colorArg = NULL; /* Color for tracking*/ 00150 char outfilename[128]; /* Name of the output "edge" image */ 00151 char composedfname[128]; /* Name of the output "direction" image */ 00152 unsigned char *edge; /* The output edge image */ 00153 float sigma, /* Standard deviation of the gaussian kernel. */ 00154 tlow, /* Fraction of the high threshold in hysteresis. */ 00155 thigh; /* High hysteresis threshold control. The actual 00156 threshold is the (100 * thigh) percentage point 00157 in the histogram of the magnitude of the 00158 gradient image that passes non-maximal 00159 suppression. */ 00160 00161 int imgW, imgH, ww, hh; 00162 Image< PixRGB<byte> > colorImg; 00163 Image<byte> grayImg; 00164 00165 Image< PixRGB<byte> > display; 00166 Image<byte> temp; 00167 Image<PixRGB<byte> > Aux; 00168 Image<PixH2SV2<float> > H2SVimage; 00169 00170 // instantiate a model manager (for camera input): 00171 ModelManager manager("Canny Tester"); 00172 // Instantiate our various ModelComponents: 00173 nub::soft_ref<FrameIstream> 00174 gb(makeIEEE1394grabber(manager, "cannycam", "cc")); 00175 /// all this should not be in global scope, but in main()! 00176 00177 segmentImageTrackMC<float,unsigned int,4> *segmenter; 00178 00179 //! Mean color to track 00180 std::vector<float> color(4,0.0F); 00181 00182 bool runCanny(bool fromFile, bool useColor, const char* shapeArg); //FIX: change later so this returns struct with found info (position, etc) 00183 00184 /************************************************************ 00185 Optimizer function Prototypes (pt2) 00186 ************************************************************/ 00187 00188 //back to being ripped from a book 00189 00190 //Note: powell and linmin now use a ShapeModel optimizee class. brent and mnbrak use func ptr to f1dim, which has copy of the optimizee 00191 bool powell(double p[], double **xi, int n, double ftol, 00192 int *iter, double *fret, rutz::shared_ptr<ShapeModel>& optimizee); 00193 int ncom; 00194 double *pcom,*xicom; 00195 00196 double brent(double ax, double bx, double cx, 00197 double (*f)(double, rutz::shared_ptr<ShapeModel>&), double tol, 00198 double *xmin, rutz::shared_ptr<ShapeModel>& shape); 00199 void mnbrak(double *ax, double *bx, double *cx, double *fa, double *fb, 00200 double *fc, double (*func)(double, rutz::shared_ptr<ShapeModel>&), 00201 rutz::shared_ptr<ShapeModel>& shape); 00202 double f1dim(double x, rutz::shared_ptr<ShapeModel>& shape); 00203 void linmin(double p[], double xi[], int n, double *fret, 00204 rutz::shared_ptr<ShapeModel>& optimizee); 00205 00206 00207 ///////////////////////FROM NRUTIL///////////////////////// 00208 00209 00210 double *nrVector(long nl, long nh) 00211 /* allocate a double vector with subscript range v[nl..nh] */ 00212 { 00213 double *v; 00214 00215 v=(double *)malloc((size_t) ((nh-nl+2)*sizeof(double))); 00216 if (!v) printf("allocation failure in nrVector()"); 00217 return v-nl+1; 00218 } 00219 00220 void free_nrVector(double *v, long nl, long nh) 00221 /* free a double vector allocated with nrVector() */ 00222 { 00223 free((FREE_ARG) (v+nl-1)); 00224 } 00225 00226 //////////////////////////MAIN///////////////////////////// 00227 00228 int main(int argc, char *argv[]) 00229 { 00230 // MYLOGVERB = 0; //sets extern value to suppress unwanted output from other modules 00231 00232 //set math details. For now, harcoded 00233 sigma = .5; 00234 tlow = .5; 00235 thigh = .5; 00236 00237 bool useColor = true; 00238 00239 manager.addSubComponent(gb); 00240 00241 00242 // set the camera number (in IEEE1394 lingo, this is the 00243 // "subchannel" number): 00244 gb->setModelParamVal("FrameGrabberSubChan", 0); 00245 00246 00247 00248 /**************************************************************************** 00249 * Get the command line arguments. 00250 ****************************************************************************/ 00251 if(argc < 3){ 00252 fprintf(stderr,"\n<USAGE> %s image shape color \n",argv[0]); 00253 fprintf(stderr,"\n image: An image to process. Must be in PGM format.\n"); 00254 fprintf(stderr," Type 'none' for camera input.\n"); 00255 fprintf(stderr," shape: Shape on which to run recognition\n"); 00256 fprintf(stderr," Candidates: Rectangle, Square, Circle, Octagon.\n"); 00257 fprintf(stderr," color: Color to track\n"); 00258 fprintf(stderr," Candidates: Blue, Yellow, none (for no color tracking).\n"); 00259 exit(1); 00260 } 00261 infilename = argv[1]; 00262 shapeArg = argv[2]; 00263 colorArg = argv[3]; 00264 00265 printf("READ: 1: %s 2: %s 3: %s\n", infilename, shapeArg, colorArg); 00266 00267 if(!strcmp(infilename, "none")){ 00268 fromFile = false; 00269 infilename = "../../bin/Camera.pgm";//hacky~ should let user say where they want file placed 00270 } 00271 00272 //what's this? 00273 if(argc == 6) dirfilename = infilename; 00274 else dirfilename = NULL; 00275 00276 00277 /*************************************************************************** 00278 * Perform color exclusion. Using grayImg for now, should likely use separate image later to preserve step output. 00279 *NOTE: a lot of code is repeated in file and camera input, because up in-if/else declaration of segmenter 00280 **************************************************************************/ 00281 00282 00283 //COLORTRACKING DECS: note that much of these may not need to be in this file 00284 //Colors MUST use H2SV2 pixel values! use test-sampleH2SV2 to sample needed values! --Z 00285 00286 //0 = H1 (0-1), 1=H2 (0-1), 2=S (0-1), 3=V (0-1) 00287 if(!strcmp(colorArg, "Red")){ 00288 //RED 00289 color[0] = 0.70000F; color[1] = 0.94400F; color[2] = 0.67200F; color[3] = 0.96900F; 00290 } 00291 00292 else if(!strcmp(colorArg, "Green")){ 00293 //Green 00294 color[0] = 0.53000F; color[1] = 0.36000F; color[2] = 0.44600F; color[3] = 0.92000F; 00295 } 00296 00297 else if(!strcmp(colorArg, "Orange")){ 00298 //"ORANGE" 00299 color[0] = 0.80000F; color[1] = 0.80000F; color[2] = 0.57600F; color[3] = 0.96800F; 00300 } 00301 else if(!strcmp(colorArg, "Blue")){ 00302 //BLUE 00303 color[0] = 0.27500F; color[1] = 0.52500F; color[2] = 0.44500F; color[3] = 0.97200F; 00304 } 00305 else if(!strcmp(colorArg, "Yellow")){ 00306 //YELLOW 00307 color[0] = 0.90000F; color[1] = 0.62000F; color[2] = 0.54000F; color[3] = 0.97000F; 00308 } 00309 else{ 00310 useColor = false; 00311 } 00312 00313 //! +/- tollerance value on mean for track 00314 std::vector<float> std(4,0.0F); 00315 //NOTE that the saturation tolerance is important (if it gos any higher thn this, it will nearly always recognize white!) 00316 std[0] = 0.30000; std[1] = 0.30000; std[2] = 0.44500; std[3] = 0.45000; 00317 00318 //! normalizer over color values (highest value possible) 00319 std::vector<float> norm(4,0.0F); 00320 norm[0] = 1.0F; norm[1] = 1.0F; norm[2] = 1.0F; norm[3] = 1.0F; 00321 00322 //! how many standard deviations out to adapt, higher means less bias 00323 //The lower these are, the more strict recognition will be in subsequent frames 00324 //TESTED AND PROVED do NOT change unless you're SURE 00325 std::vector<float> adapt(4,0.0F); 00326 //adapt[0] = 3.5F; adapt[1] = 3.5F; adapt[2] = 3.5F; adapt[3] = 3.5F; 00327 adapt[0] = 5.0F; adapt[1] = 5.0F; adapt[2] = 5.0F; adapt[3] = 5.0F; 00328 00329 //! highest value for color adaptation possible (hard boundry) 00330 std::vector<float> upperBound(4,0.0F); 00331 upperBound[0] = color[0] + 0.45F; upperBound[1] = color[1] + 0.45F; upperBound[2] = color[2] + 0.55F; upperBound[3] = color[3] + 0.55F; 00332 00333 //! lowest value for color adaptation possible (hard boundry) 00334 std::vector<float> lowerBound(4,0.0F); 00335 lowerBound[0] = color[0] - 0.45F; lowerBound[1] = color[1] - 0.45F; lowerBound[2] = color[2] - 0.55F; lowerBound[3] = color[3] - 0.55F; 00336 //END DECS 00337 00338 //set up image details 00339 if(fromFile){ 00340 colorImg = Raster::ReadRGB(infilename); 00341 imgW = colorImg.getWidth(); 00342 imgH = colorImg.getHeight(); 00343 } 00344 else{ 00345 /************************************************** 00346 CAMERA INPUT 00347 -Code ripped from test-grab. Forces to IEEE1394. has short stream for buffering and reads in pic from camera, then starts powell minimization, which displays in new window. 00348 **************************************************/ 00349 00350 imgH = gb->getHeight(); 00351 imgW = gb->getWidth(); 00352 00353 } 00354 00355 segmenter = new segmentImageTrackMC<float,unsigned int,4> (imgW*imgH); 00356 //color tracking stuff 00357 segmenter->SITsetTrackColor(&color,&std,&norm,&adapt,&upperBound,&lowerBound); 00358 00359 ww = imgW/4; 00360 hh = imgH/4; 00361 00362 segmenter->SITsetFrame(&ww,&hh); 00363 00364 // Set display colors for output of tracking. Strictly aesthetic //TEST 00365 //And definitely not needed, although for some reason it's required --Z 00366 segmenter->SITsetCircleColor(0,255,0); 00367 segmenter->SITsetBoxColor(255,255,0,0,255,255); 00368 segmenter->SITsetUseSmoothing(true,10); 00369 00370 //method provided by Nathan to turn off adaptation (useful for dynamic environments) 00371 segmenter->SITtoggleColorAdaptation(false); 00372 //enable single-frame tracking 00373 segmenter->SITtoggleCandidateBandPass(false); 00374 00375 Image<PixRGB<byte> > Aux; 00376 Aux.resize(100,450,true); 00377 Image<byte> temp; 00378 Image< PixRGB<byte> > display = colorImg; 00379 00380 if(fromFile){ //for input from file 00381 00382 if(useColor){ 00383 segmenter->SITtrackImage(colorImg,&display,&Aux,true); 00384 temp = segmenter->SITreturnCandidateImage();//get image we'll be using for edge detection 00385 00386 grayImg = luminance(temp); 00387 } 00388 else{ 00389 grayImg = luminance(colorImg); 00390 } 00391 } 00392 else{ 00393 // get ready for main loop: 00394 win.reset( new XWindow(gb->peekDims(), -1, -1, "test-canny window") ); 00395 //color tracking stuffages 00396 segmenter->SITsetTrackColor(&color,&std,&norm,&adapt,&upperBound,&lowerBound); 00397 ww = imgW/4; 00398 hh = imgH/4; 00399 segmenter->SITsetFrame(&ww,&hh); 00400 // Set display colors for output of tracking. Strictly asthetic //TEST 00401 //And definitely not needed, although for soe reason it's required --Z 00402 segmenter->SITsetCircleColor(0,255,0); 00403 segmenter->SITsetBoxColor(255,255,0,0,255,255); 00404 segmenter->SITsetUseSmoothing(true,10); 00405 00406 Aux.resize(100,450,true); 00407 00408 } 00409 00410 if(fromFile){ 00411 runCanny(fromFile, useColor, shapeArg); 00412 } 00413 else{ 00414 00415 // let's get all our ModelComponent instances started: 00416 manager.start(); 00417 00418 // get the frame grabber to start streaming: 00419 gb->startStream(); 00420 00421 colorImg = gb->readRGB(); 00422 00423 xwin.reset(new XWindow(colorImg.getDims())); 00424 xwin->setPosition(colorImg.getWidth(), 0); 00425 while(1){ 00426 runCanny(fromFile, useColor, shapeArg); 00427 } 00428 00429 manager.stop(); 00430 } 00431 } 00432 00433 bool runCanny(bool fromFile, bool useColor, const char* shapeArg){ 00434 00435 if(!fromFile){ 00436 int i; 00437 for(i = 0; i < 30; i++) {//give camera holder time to aim 00438 00439 colorImg = gb->readRGB(); 00440 win->drawImage(colorImg); 00441 } 00442 00443 //Get candidate image representing blobs of desired color 00444 H2SVimage = colorImg;//changed since transfer to H2SV 00445 display = colorImg; 00446 segmenter->SITtrackImageAny(H2SVimage,&display,&Aux,true); 00447 temp = segmenter->SITreturnCandidateImage();//get image we'll be using for edge detection 00448 00449 //set up structuring elements (disk) 00450 Image<byte> se1(5, 5, ZEROS); 00451 Point2D<int> t1(2,2); 00452 drawDisk(se1, t1, 2, static_cast<byte>(255)); 00453 00454 Image<byte> se2(9, 9, ZEROS); 00455 Point2D<int> t2(4,4); 00456 drawDisk(se2, t2, 3, static_cast<byte>(255)); 00457 00458 //Erode image to prevent noise NOTE: isn't working well, erodes too much, probably because temp is so lowrez 00459 //temp = erodeImg(temp, se1); 00460 00461 //Close candidate image to prevent holes 00462 temp = closeImg(temp, se2); 00463 00464 if(useColor){ 00465 grayImg = (Image<byte>)(quickInterpolate(temp,4));//note that this is very low rez 00466 } 00467 else{ 00468 grayImg = luminance(colorImg); 00469 } 00470 //END CAMERA 00471 } 00472 /**************************************************************************** 00473 * Perform the edge detection. 00474 ****************************************************************************/ 00475 /* 00476 if(dirfilename != NULL){ 00477 if(!fromFile){ 00478 sprintf(composedfname, "%s_s_%3.2f_l_%3.2f_h_%3.2f.fim", infilename, 00479 sigma, tlow, thigh); 00480 } 00481 else{ 00482 sprintf(composedfname, "%s_s_%3.2f_l_%3.2f_h_%3.2f.fim", "Camera", 00483 sigma, tlow, thigh); 00484 } 00485 dirfilename = composedfname; 00486 } 00487 */ 00488 canny(grayImg.getArrayPtr(), grayImg.getHeight(), grayImg.getWidth(), sigma, tlow, thigh, &edge, dirfilename); 00489 00490 00491 /**************************************************************************** 00492 * Write out the edge image to a file. 00493 *****************************************************************************/ 00494 /* 00495 if(!fromFile){ 00496 sprintf(outfilename, "%s_s_%3.2f_l_%3.2f_h_%3.2f.pgm", infilename, sigma, tlow, thigh); 00497 } 00498 else{ 00499 sprintf(outfilename, "%s_s_%3.2f_l_%3.2f_h_%3.2f.pgm", "Camera", sigma, tlow, thigh); 00500 } 00501 */ 00502 Image<unsigned char> cannyCharImg(edge, grayImg.getWidth(), grayImg.getHeight()); 00503 00504 Image<float> cannyImg = cannyCharImg; 00505 00506 Raster::WriteGray(cannyImg, outfilename, RASFMT_PNM);//edge image 00507 00508 xwin->drawImage((Image< PixRGB<byte> >)grayImg); 00509 00510 Point2D<int> cent = centroid(cannyCharImg); 00511 distMap = chamfer34(cannyImg, 5000.0f); 00512 //END EDGE DETECTION 00513 00514 //PERFORM SHAPE DETECTION 00515 rutz::shared_ptr<ShapeModel> shape; 00516 double* p = NULL; 00517 00518 int iter = 0; 00519 double fret = 0.0; 00520 bool shapeFound = false; 00521 00522 //NOTE: following bracket set is to define scope outside which the shape model will be destroyed 00523 { 00524 /*********************************************** 00525 *Set values for the model and declare it here 00526 Common Values: 00527 x_center: initial central x position of the model 00528 y_center: initial central y position of the model 00529 -in the future, try multiple arrangements of these, 00530 perhaps in some meta-omptimizer, since noisy 00531 images are sensitive to these values 00532 height/width/etc: initial dimensions of the model. 00533 Be careful to note if ratio is to be fixed or free! 00534 alpha: initial angular orientation of the model 00535 **********************************************/ 00536 00537 if(!strcmp(shapeArg, "Rectangle")){ 00538 //rectangle 00539 //NOTE: the order of dimensions is changed. This is a test. May need to change thm back 00540 00541 p = (double*)calloc(6, sizeof(double)); 00542 p[1] = (double)(cent.i); //Xcenter 00543 p[2] = (double)(cent.j); //Ycenter 00544 p[4] = 170.0f; // Width 00545 p[5] = 140.0f; // Height 00546 p[3] = 0.0; //alpha 00547 00548 shape.reset(new RectangleShape(120.0, p, true)); 00549 } 00550 else if(!strcmp(shapeArg, "Square")){ 00551 //try it with a square 00552 00553 p = (double*)calloc(5, sizeof(double)); 00554 p[1] = (double)(cent.i); //Xcenter 00555 p[2] = (double)(cent.j); //Ycenter 00556 p[3] = 100.0f; // Height 00557 p[4] = 0.0; // alpha 00558 00559 shape.reset(new SquareShape(100.0, p, true)); 00560 } 00561 else if(!strcmp(shapeArg, "Octagon")){ 00562 //try it with an octagon 00563 00564 p = (double*)calloc(5, sizeof(double)); 00565 p[1] = (double)(cent.i); //Xcenter 00566 p[2] = (double)(cent.j); //Ycenter 00567 p[3] = 80.0f; // Height 00568 p[4] = 0.0; // alpha 00569 00570 shape.reset(new OctagonShape(80.0, p, true)); 00571 } 00572 // else if(!strcmp(shapeArg, "Parallel")){ 00573 // //parallel lines (SUCKS for now) 00574 // p[1] = (double)(cent.i); //Xcenter 00575 // p[2] = (double)(cent.j); //Ycenter 00576 // p[3] = 50.0f; // Width 00577 // p[4] = 160.0f; // Height 00578 // p[5] = 0.0; //alpha 00579 00580 // shape = new ParallelShape(40.0, true); 00581 // } 00582 else if(!strcmp(shapeArg, "Circle")){ 00583 //circle 00584 p = (double*)calloc(4, sizeof(double)); 00585 p[1] = (double)(cent.i); //Xcenter 00586 p[2] = (double)(cent.j); //Ycenter 00587 p[3] = 50.0f; // Radius 00588 00589 00590 shape.reset(new CircleShape(40.0, p, true)); 00591 } 00592 else{ 00593 printf("Cannot run shape recognition without a shape to recognize! Returning...\n"); 00594 shape.reset(new CircleShape(9999.0, p, false)); 00595 return(false); 00596 } 00597 00598 double** xi = (double**)calloc(shape->getNumDims(), sizeof(double)); 00599 00600 //NOTE: HERE is where the initial direction matrix is built. Change these values to change axis assignment 00601 for(int i=0; i < shape->getNumDims(); i++) { 00602 xi[i] = (double*)calloc(shape->getNumDims(), sizeof(double)); 00603 xi[i][i] = 1.0; 00604 } 00605 //TEST axes 00606 xi[0][0] = 0.5; 00607 xi[0][1] = 0.5; 00608 00609 //here, we will be using powell classes, and, instead of passing in calcDist, will be passing in a shape. 00610 shapeFound = powell(shape->getDimensions(), xi, shape->getNumDims(), 0.5, &iter, &fret, shape); 00611 if(shapeFound){ 00612 p = shape->getDimensions();//TEST 00613 printf("Shape found!\n"); 00614 printf("Final dimensions are -- "); 00615 for(int w = 1; w <=shape->getNumDims(); w++){ 00616 printf("%d: %f ", w, p[w]);//outputs final values 00617 } 00618 printf("Certainty (lower is better): %f\n", fret); 00619 }else{ 00620 printf("Shape not found -- certainty was %f\n", fret); 00621 } 00622 } 00623 //commenting out annoying outputs -- Z 00624 00625 //printf("%f %f %f %f %f %f\n", p[0], p[1], p[2], p[3], p[4], fret); 00626 //sprintf(outfilename, "%s_d_%3.2f_l_%3.2f_h_%3.2f.pgm", infilename, sigma, tlow, thigh); 00627 //Raster::WriteFloat(distMap, FLOAT_NORM_0_255, outfilename);//distance transfer 00628 00629 return(shapeFound); 00630 } 00631 00632 void mnbrak(double *ax, double *bx, double *cx, double *fa, double *fb, 00633 double *fc, double (*func)(double, rutz::shared_ptr<ShapeModel>& ), 00634 rutz::shared_ptr<ShapeModel>& shape) 00635 { 00636 double ulim,u,r,q,fu,dum; 00637 00638 *fa=(*func)(*ax, shape); 00639 *fb=(*func)(*bx, shape); 00640 if (*fb > *fa) { 00641 SHFT(dum,*ax,*bx,dum) 00642 SHFT(dum,*fb,*fa,dum) 00643 } 00644 *cx=(*bx)+GOLD*(*bx-*ax); 00645 *fc=(*func)(*cx, shape); 00646 while (*fb > *fc) { 00647 r=(*bx-*ax)*(*fb-*fc); 00648 q=(*bx-*cx)*(*fb-*fa); 00649 u=(*bx)-((*bx-*cx)*q-(*bx-*ax)*r)/ 00650 (2.0*SIGN(std::max(fabs(q-r),TINY),q-r)); 00651 ulim=(*bx)+GLIMIT*(*cx-*bx); 00652 if ((*bx-u)*(u-*cx) > 0.0) { 00653 fu=(*func)(u, shape); 00654 if (fu < *fc) { 00655 *ax=(*bx); 00656 *bx=u; 00657 *fa=(*fb); 00658 *fb=fu; 00659 return; 00660 } else if (fu > *fb) { 00661 *cx=u; 00662 *fc=fu; 00663 return; 00664 } 00665 u=(*cx)+GOLD*(*cx-*bx); 00666 fu=(*func)(u, shape); 00667 } else if ((*cx-u)*(u-ulim) > 0.0) { 00668 fu=(*func)(u, shape); 00669 if (fu < *fc) { 00670 SHFT(*bx,*cx,u,*cx+GOLD*(*cx-*bx)) 00671 SHFT(*fb,*fc,fu,(*func)(u, shape)) 00672 } 00673 } else if ((u-ulim)*(ulim-*cx) >= 0.0) { 00674 u=ulim; 00675 fu=(*func)(u, shape); 00676 } else { 00677 u=(*cx)+GOLD*(*cx-*bx); 00678 fu=(*func)(u, shape); 00679 } 00680 SHFT(*ax,*bx,*cx,u) 00681 SHFT(*fa,*fb,*fc,fu) 00682 } 00683 } 00684 00685 double brent(double ax, double bx, double cx, double (*f)(double, rutz::shared_ptr<ShapeModel>&), double tol, 00686 double *xmin, rutz::shared_ptr<ShapeModel>& shape) 00687 { 00688 int iter; 00689 double a,b,d=0.0,etemp,fu,fv,fw,fx,p,q,r,tol1,tol2,u,v,w,x,xm; 00690 double e=0.0; 00691 00692 a=(ax < cx ? ax : cx); 00693 b=(ax > cx ? ax : cx); 00694 x=w=v=bx; 00695 fw=fv=fx=(*f)(x, shape); 00696 for (iter=1;iter<=ITMAXB;iter++) { 00697 xm=0.5*(a+b); 00698 tol2=2.0*(tol1=tol*fabs(x)+ZEPS); 00699 if (fabs(x-xm) <= (tol2-0.5*(b-a))) { 00700 *xmin=x; 00701 return fx; 00702 } 00703 if (fabs(e) > tol1) { 00704 r=(x-w)*(fx-fv); 00705 q=(x-v)*(fx-fw); 00706 p=(x-v)*q-(x-w)*r; 00707 q=2.0*(q-r); 00708 if (q > 0.0) p = -p; 00709 q=fabs(q); 00710 etemp=e; 00711 e=d; 00712 if (fabs(p) >= fabs(0.5*q*etemp) || p <= q*(a-x) || p >= q*(b-x)) 00713 d=CGOLD*(e=(x >= xm ? a-x : b-x)); 00714 else { 00715 d=p/q; 00716 u=x+d; 00717 if (u-a < tol2 || b-u < tol2) 00718 d=SIGN(tol1,xm-x); 00719 } 00720 } else { 00721 d=CGOLD*(e=(x >= xm ? a-x : b-x)); 00722 } 00723 u=(fabs(d) >= tol1 ? x+d : x+SIGN(tol1,d)); 00724 fu=(*f)(u, shape); 00725 if (fu <= fx) { 00726 if (u >= x) a=x; else b=x; 00727 SHFT(v,w,x,u) 00728 SHFT(fv,fw,fx,fu) 00729 } else { 00730 if (u < x) a=u; else b=u; 00731 if (fu <= fw || w == x) { 00732 v=w; 00733 w=u; 00734 fv=fw; 00735 fw=fu; 00736 } else if (fu <= fv || v == x || v == w) { 00737 v=u; 00738 fv=fu; 00739 } 00740 } 00741 } 00742 printf("Too many iterations in brent"); 00743 *xmin=x; 00744 return fx; 00745 } 00746 00747 //Changed to run dim on ShapeModel 00748 double f1dim(double x, rutz::shared_ptr<ShapeModel>& shape) 00749 { 00750 int j; 00751 double f, *xt; 00752 xt=nrVector(1,ncom); 00753 00754 for (j=1;j<=ncom;j++) xt[j]=pcom[j]+x*xicom[j]; 00755 f=shape->calcDist(xt, distMap); 00756 free_nrVector(xt,1,ncom); 00757 return f; 00758 } 00759 00760 //Changed to accept ShapeModel optimizee class 00761 void linmin(double p[], double xi[], int n, double *fret, 00762 rutz::shared_ptr<ShapeModel>& optimizee) 00763 { 00764 int j; 00765 double xx,xmin,fx,fb,fa,bx,ax; 00766 00767 ncom=n; 00768 pcom=nrVector(1,n); 00769 xicom=nrVector(1,n); 00770 for (j=1;j<=n;j++) { 00771 pcom[j]=p[j]; 00772 xicom[j]=xi[j]; 00773 } 00774 00775 ax=0.0; 00776 xx=1.0; 00777 00778 mnbrak(&ax,&xx,&bx,&fa,&fx,&fb,f1dim,optimizee); 00779 *fret=brent(ax,xx,bx,f1dim,TOL,&xmin, optimizee); 00780 for (j=1;j<=n;j++) { 00781 xi[j] *= xmin; 00782 p[j] += xi[j]; 00783 } 00784 free_nrVector(xicom,1,n); 00785 free_nrVector(pcom,1,n); 00786 } 00787 00788 //Changed to accept ShapeModel optimizee class 00789 bool powell(double p[], double **xi, int n, double ftol, 00790 int *iter, double *fret, rutz::shared_ptr<ShapeModel>& optimizee) 00791 { 00792 int i,ibig,j; 00793 double del,fp,fptt,t, *pt, *ptt, *xit; 00794 00795 pt=nrVector(1,n); 00796 ptt=nrVector(1,n); 00797 xit=nrVector(1,n); 00798 00799 fptt = optimizee->getThreshold(); //hacky, to prevent warning 00800 00801 *fret=optimizee->calcDist(p, distMap); 00802 for (j=1;j<=n;j++) pt[j]=p[j]; 00803 for (*iter=1;;++(*iter)) { 00804 fp=(*fret); 00805 ibig=0; 00806 del=0.0; 00807 for (i=1;i<=n;i++) { 00808 for (j = 1; j <= n; j++){ 00809 //NOTE: This stupid change using -1 for the xi vector is necessary to handle weirdness of the nrVectors 00810 xit[j] = xi[j-1][i-1]; 00811 } 00812 fptt=(*fret); 00813 linmin(p,xit,n,fret,optimizee); 00814 if (fabs(fptt-(*fret)) > del) { 00815 del=fabs(fptt-(*fret)); 00816 ibig=i; 00817 } 00818 } 00819 if (2.0*fabs(fp-(*fret)) <= ftol*(fabs(fp)+fabs(*fret))) { 00820 00821 //If cost is below threshold, then return that the shape was found 00822 //perhaps this is not in the most ideal spot. --Z 00823 if(fptt < optimizee->getThreshold()){/*if cost estimate is below threshold, shape is present in 00824 image (very rudimentary, need to take dimensions into account)*/ 00825 return(true);//test 00826 } 00827 //otherwise, return that it was not 00828 else{return(false);}//test 00829 } 00830 if (*iter == ITMAX){ 00831 return(false);//test 00832 printf("powell exceeding maximum iterations."); 00833 } 00834 for (j=1;j<=n;j++) { 00835 ptt[j]=2.0*p[j]-pt[j]; 00836 xit[j]=p[j]-pt[j]; 00837 pt[j]=p[j]; 00838 } 00839 fptt=optimizee->calcDist(ptt, distMap); 00840 if (fptt < fp) { 00841 t=2.0*(fp-2.0*(*fret)+fptt)*squareOf(fp-(*fret)-del)-del*squareOf(fp-fptt); 00842 if (t < 0.0) { 00843 linmin(p,xit,n,fret,optimizee); 00844 for (j=0;j<n;j++) { 00845 xi[j][ibig]=xi[j][n]; 00846 xi[j][n]=xit[j]; 00847 } 00848 } 00849 } 00850 00851 } 00852 } 00853 00854 /******************************************************************************* 00855 * PROCEDURE: canny 00856 * PURPOSE: To perform canny edge detection. 00857 * NAME: Mike Heath 00858 * DATE: 2/15/96 00859 //Pradeep: returns the centroid of the "white" pixels 00860 *******************************************************************************/ 00861 int canny(unsigned char *image, int rows, int cols, float sigma, 00862 float tlow, float thigh, unsigned char **edge, const char* fname) 00863 { 00864 FILE *fpdir=NULL; /* File to write the gradient image to. */ 00865 unsigned char *nms; /* Points that are local maximal magnitude. */ 00866 short int *smoothedim, /* The image after gaussian smoothing. */ 00867 *delta_x, /* The first devivative image, x-direction. */ 00868 *delta_y, /* The first derivative image, y-direction. */ 00869 *magnitude; /* The magnitude of the gadient image. */ 00870 float *dir_radians=NULL; /* Gradient direction image. */ 00871 00872 /**************************************************************************** 00873 * Perform gaussian smoothing on the image using the input standard 00874 * deviation. 00875 ****************************************************************************/ 00876 gaussian_smooth(image, rows, cols, sigma, &smoothedim); 00877 00878 /**************************************************************************** 00879 * Compute the first derivative in the x and y directions. 00880 ****************************************************************************/ 00881 derrivative_x_y(smoothedim, rows, cols, &delta_x, &delta_y); 00882 00883 /**************************************************************************** 00884 * This option to write out the direction of the edge gradient was added 00885 * to make the information available for computing an edge quality figure 00886 * of merit. 00887 ****************************************************************************/ 00888 if(fname != NULL){ 00889 /************************************************************************* 00890 * Compute the direction up the gradient, in radians that are 00891 * specified counteclockwise from the positive x-axis. 00892 *************************************************************************/ 00893 radian_direction(delta_x, delta_y, rows, cols, &dir_radians, -1, -1); 00894 00895 /************************************************************************* 00896 * Write the gradient direction image out to a file. 00897 *************************************************************************/ 00898 if((fpdir = fopen(fname, "wb")) == NULL){ 00899 fprintf(stderr, "Error opening the file %s for writing.\n", fname); 00900 exit(1); 00901 } 00902 fwrite(dir_radians, sizeof(float), rows*cols, fpdir); 00903 fclose(fpdir); 00904 free(dir_radians); 00905 } 00906 00907 /**************************************************************************** 00908 * Compute the magnitude of the gradient. 00909 ****************************************************************************/ 00910 magnitude_x_y(delta_x, delta_y, rows, cols, &magnitude); 00911 00912 /**************************************************************************** 00913 * Perform non-maximal suppression. 00914 ****************************************************************************/ 00915 if((nms = (unsigned char *) calloc(rows*cols,sizeof(unsigned char)))==NULL){ 00916 fprintf(stderr, "Error allocating the nms image.\n"); 00917 exit(1); 00918 } 00919 00920 non_max_supp(magnitude, delta_x, delta_y, rows, cols, nms); 00921 00922 /**************************************************************************** 00923 * Use hysteresis to mark the edge pixels. 00924 ****************************************************************************/ 00925 if((*edge=(unsigned char *)calloc(rows*cols,sizeof(unsigned char))) ==NULL){ 00926 fprintf(stderr, "Error allocating the edge image.\n"); 00927 exit(1); 00928 } 00929 //printf("\n%d %d %d %d %d %d\n", magnitude, nms, rows, cols, tlow, thigh); 00930 int centroid = apply_hysteresis(magnitude, nms, rows, cols, tlow, thigh, *edge); 00931 /**************************************************************************** 00932 * Free all of the memory that we allocated except for the edge image that 00933 * is still being used to store out result. 00934 ****************************************************************************/ 00935 free(smoothedim); 00936 free(delta_x); 00937 free(delta_y); 00938 free(magnitude); 00939 free(nms); 00940 return centroid; 00941 } 00942 00943 /******************************************************************************* 00944 * Procedure: radian_direction 00945 * Purpose: To compute a direction of the gradient image from component dx and 00946 * dy images. Because not all derriviatives are computed in the same way, this 00947 * code allows for dx or dy to have been calculated in different ways. 00948 * 00949 * FOR X: xdirtag = -1 for [-1 0 1] 00950 * xdirtag = 1 for [ 1 0 -1] 00951 * 00952 * FOR Y: ydirtag = -1 for [-1 0 1]' 00953 * ydirtag = 1 for [ 1 0 -1]' 00954 * 00955 * The resulting angle is in radians measured counterclockwise from the 00956 * xdirection. The angle points "up the gradient". 00957 *******************************************************************************/ 00958 void radian_direction(short int *delta_x, short int *delta_y, int rows, 00959 int cols, float **dir_radians, int xdirtag, int ydirtag) 00960 { 00961 int r, c, pos; 00962 float *dirim=NULL; 00963 double dx, dy; 00964 00965 /**************************************************************************** 00966 * Allocate an image to store the direction of the gradient. 00967 ****************************************************************************/ 00968 if((dirim = (float *) calloc(rows*cols, sizeof(float))) == NULL){ 00969 fprintf(stderr, "Error allocating the gradient direction image.\n"); 00970 exit(1); 00971 } 00972 *dir_radians = dirim; 00973 00974 for(r=0,pos=0;r<rows;r++){ 00975 for(c=0;c<cols;c++,pos++){ 00976 dx = (double)delta_x[pos]; 00977 dy = (double)delta_y[pos]; 00978 00979 if(xdirtag == 1) dx = -dx; 00980 if(ydirtag == -1) dy = -dy; 00981 00982 dirim[pos] = (float)angle_radians(dx, dy); 00983 } 00984 } 00985 } 00986 00987 /******************************************************************************* 00988 * FUNCTION: angle_radians 00989 * PURPOSE: This procedure computes the angle of a vector with components x and 00990 * y. It returns this angle in radians with the answer being in the range 00991 * 0 <= angle <2*PI. 00992 *******************************************************************************/ 00993 double angle_radians(double x, double y) 00994 { 00995 double xu, yu, ang; 00996 00997 xu = fabs(x); 00998 yu = fabs(y); 00999 01000 if((xu == 0) && (yu == 0)) return(0); 01001 01002 ang = atan(yu/xu); 01003 01004 if(x >= 0){ 01005 if(y >= 0) return(ang); 01006 else return(2*M_PI - ang); 01007 } 01008 else{ 01009 if(y >= 0) return(M_PI - ang); 01010 else return(M_PI + ang); 01011 } 01012 } 01013 01014 /******************************************************************************* 01015 * PROCEDURE: magnitude_x_y 01016 * PURPOSE: Compute the magnitude of the gradient. This is the square root of 01017 * the sum of the squared derivative values. 01018 * NAME: Mike Heath 01019 * DATE: 2/15/96 01020 *******************************************************************************/ 01021 void magnitude_x_y(short int *delta_x, short int *delta_y, int rows, int cols, 01022 short int **magnitude) 01023 { 01024 int r, c, pos, sq1, sq2; 01025 01026 /**************************************************************************** 01027 * Allocate an image to store the magnitude of the gradient. 01028 ****************************************************************************/ 01029 if((*magnitude = (short *) calloc(rows*cols, sizeof(short))) == NULL){ 01030 fprintf(stderr, "Error allocating the magnitude image.\n"); 01031 exit(1); 01032 } 01033 01034 for(r=0,pos=0;r<rows;r++){ 01035 for(c=0;c<cols;c++,pos++){ 01036 sq1 = (int)delta_x[pos] * (int)delta_x[pos]; 01037 sq2 = (int)delta_y[pos] * (int)delta_y[pos]; 01038 (*magnitude)[pos] = (short)(0.5 + sqrt((float)sq1 + (float)sq2)); 01039 } 01040 } 01041 01042 } 01043 01044 /******************************************************************************* 01045 * PROCEDURE: derrivative_x_y 01046 * PURPOSE: Compute the first derivative of the image in both the x any y 01047 * directions. The differential filters that are used are: 01048 * 01049 * -1 01050 * dx = -1 0 +1 and dy = 0 01051 * +1 01052 * 01053 * NAME: Mike Heath 01054 * DATE: 2/15/96 01055 *******************************************************************************/ 01056 void derrivative_x_y(short int *smoothedim, int rows, int cols, 01057 short int **delta_x, short int **delta_y) 01058 { 01059 int r, c, pos; 01060 01061 /**************************************************************************** 01062 * Allocate images to store the derivatives. 01063 ****************************************************************************/ 01064 if(((*delta_x) = (short *) calloc(rows*cols, sizeof(short))) == NULL){ 01065 fprintf(stderr, "Error allocating the delta_x image.\n"); 01066 exit(1); 01067 } 01068 if(((*delta_y) = (short *) calloc(rows*cols, sizeof(short))) == NULL){ 01069 fprintf(stderr, "Error allocating the delta_x image.\n"); 01070 exit(1); 01071 } 01072 01073 /**************************************************************************** 01074 * Compute the x-derivative. Adjust the derivative at the borders to avoid 01075 * losing pixels. 01076 ****************************************************************************/ 01077 for(r=0;r<rows;r++){ 01078 pos = r * cols; 01079 (*delta_x)[pos] = smoothedim[pos+1] - smoothedim[pos]; 01080 pos++; 01081 for(c=1;c<(cols-1);c++,pos++){ 01082 (*delta_x)[pos] = smoothedim[pos+1] - smoothedim[pos-1]; 01083 } 01084 (*delta_x)[pos] = smoothedim[pos] - smoothedim[pos-1]; 01085 } 01086 01087 /**************************************************************************** 01088 * Compute the y-derivative. Adjust the derivative at the borders to avoid 01089 * losing pixels. 01090 ****************************************************************************/ 01091 for(c=0;c<cols;c++){ 01092 pos = c; 01093 (*delta_y)[pos] = smoothedim[pos+cols] - smoothedim[pos]; 01094 pos += cols; 01095 for(r=1;r<(rows-1);r++,pos+=cols){ 01096 (*delta_y)[pos] = smoothedim[pos+cols] - smoothedim[pos-cols]; 01097 } 01098 (*delta_y)[pos] = smoothedim[pos] - smoothedim[pos-cols]; 01099 } 01100 } 01101 01102 /******************************************************************************* 01103 * PROCEDURE: gaussian_smooth 01104 * PURPOSE: Blur an image with a gaussian filter. 01105 * NAME: Mike Heath 01106 * DATE: 2/15/96 01107 *******************************************************************************/ 01108 void gaussian_smooth(unsigned char *image, int rows, int cols, float sigma, 01109 short int **smoothedim) 01110 { 01111 int r, c, rr, cc, /* Counter variables. */ 01112 windowsize, /* Dimension of the gaussian kernel. */ 01113 center; /* Half of the windowsize. */ 01114 float *tempim, /* Buffer for separable filter gaussian smoothing. */ 01115 *kernel, /* A one dimensional gaussian kernel. */ 01116 dot, /* Dot product summing variable. */ 01117 sum; /* Sum of the kernel weights variable. */ 01118 01119 /**************************************************************************** 01120 * Create a 1-dimensional gaussian smoothing kernel. 01121 ****************************************************************************/ 01122 make_gaussian_kernel(sigma, &kernel, &windowsize); 01123 center = windowsize / 2; 01124 01125 /**************************************************************************** 01126 * Allocate a temporary buffer image and the smoothed image. 01127 ****************************************************************************/ 01128 if((tempim = (float *) calloc(rows*cols, sizeof(float))) == NULL){ 01129 fprintf(stderr, "Error allocating the buffer image.\n"); 01130 exit(1); 01131 } 01132 if(((*smoothedim) = (short int *) calloc(rows*cols, 01133 sizeof(short int))) == NULL){ 01134 fprintf(stderr, "Error allocating the smoothed image.\n"); 01135 exit(1); 01136 } 01137 01138 /**************************************************************************** 01139 * Blur in the x - direction. 01140 ****************************************************************************/ 01141 for(r=0;r<rows;r++){ 01142 for(c=0;c<cols;c++){ 01143 dot = 0.0; 01144 sum = 0.0; 01145 for(cc=(-center);cc<=center;cc++){ 01146 if(((c+cc) >= 0) && ((c+cc) < cols)){ 01147 dot += (float)image[r*cols+(c+cc)] * kernel[center+cc]; 01148 sum += kernel[center+cc]; 01149 } 01150 } 01151 tempim[r*cols+c] = dot/sum; 01152 } 01153 } 01154 01155 /**************************************************************************** 01156 * Blur in the y - direction. 01157 ****************************************************************************/ 01158 for(c=0;c<cols;c++){ 01159 for(r=0;r<rows;r++){ 01160 sum = 0.0; 01161 dot = 0.0; 01162 for(rr=(-center);rr<=center;rr++){ 01163 if(((r+rr) >= 0) && ((r+rr) < rows)){ 01164 dot += tempim[(r+rr)*cols+c] * kernel[center+rr]; 01165 sum += kernel[center+rr]; 01166 } 01167 } 01168 (*smoothedim)[r*cols+c] = (short int)(dot*BOOSTBLURFACTOR/sum + 0.5); 01169 } 01170 } 01171 01172 free(tempim); 01173 free(kernel); 01174 } 01175 01176 /******************************************************************************* 01177 * PROCEDURE: make_gaussian_kernel 01178 * PURPOSE: Create a one dimensional gaussian kernel. 01179 * NAME: Mike Heath 01180 * DATE: 2/15/96 01181 *******************************************************************************/ 01182 void make_gaussian_kernel(float sigma, float **kernel, int *windowsize) 01183 { 01184 int i, center; 01185 float x, fx, sum=0.0; 01186 01187 *windowsize = int(1 + 2 * ceil(2.5 * sigma)); 01188 center = (*windowsize) / 2; 01189 01190 if((*kernel = (float *) calloc((*windowsize), sizeof(float))) == NULL){ 01191 fprintf(stderr, "Error callocing the gaussian kernel array.\n"); 01192 exit(1); 01193 } 01194 01195 for(i=0;i<(*windowsize);i++){ 01196 x = (float)(i - center); 01197 fx = pow(2.71828, -0.5*x*x/(sigma*sigma)) / (sigma * sqrt(6.2831853)); 01198 (*kernel)[i] = fx; 01199 sum += fx; 01200 } 01201 01202 for(i=0;i<(*windowsize);i++) (*kernel)[i] /= sum; 01203 01204 }