1: #include <stdlib.h> 2: #include <stdio.h> 3: #include <string.h> 4: #include <math.h> 5: #include <GL/glut.h> 6: 7: #define Round(v) ((int)(v+0.5)) 8: #define MAX 800 9: #define MAX_FILE_LENGTH 256 10: 11: #define LOW_VALUE 0 12: #define HIGH_VALUE 255 13: 14: #define TRUE 1 15: #define FALSE 0 16: 17: /*cartesian coordinate type*/ 18: typedef struct {int x; 19: int y;}coord; 20: 21: /*RGB color struct with floats*/ 22: /*typedef struct {float red; 23: float green; 24: float blue; 25: }RGB; 26: */ 27: /*RGB color struct with ints*/ 28: typedef struct {int red; 29: int green; 30: int blue; 31: }RGB_INT; 32: 33: /*number of chain codes stored, */ 34: #define MAX_NUMBER_OF_CHAINS 25 35: #define MAX_CHAINS MAX_NUMBER_OF_CHAINS 36: 37: /*minimal size to consider an "object" as length of chaincode*/ 38: #define DECENT_SIZED_OBJECT 100 39: 40: #define ONE_THIRD (1.0 / 3.0) 41: #define ONE_HALF (1.0 / 2.0) 42: #define ONE_SIXTH (1.0 / 6.0) 43: 44: /*These constant values are the chain code directions*/ 45: #define NONE -1 46: #define EAST 0 47: #define NORTHEAST 1 48: #define NORTH 2 49: #define NORTHWEST 3 50: #define WEST 4 51: #define SOUTHWEST 5 52: #define SOUTH 6 53: #define SOUTHEAST 7 54: 55: typedef struct chainCode* chain_t; 56: typedef struct chainCode 57: { 58: chain_t prev; 59: int code; 60: coord location; /*absolute pixel location for starting point*/ 61: chain_t next; 62: }chainCode; /*This struct can be refered to by: struct chainCode or chainCode*/ 63: 64: struct PGMstructure 65: { 66: int maxVal; 67: int width; 68: int height; 69: RGB_INT data[MAX][MAX]; 70: }; 71: 72: typedef struct PGMstructure PGMImage; 73: 74: /*Evil globals, but not do-able otherwise.*/ 75: static PGMImage *img_cur; 76: static PGMImage *img0; /*original*/ 77: static PGMImage *img1; /*current*/ 78: static PGMImage *img2; /*current*/ 79: static int HSIZE; 80: static int VSIZE; 81: static int MVAL; 82: 83: const RGB_INT white = {255, 255, 255}; 84: const RGB_INT yellow = {255, 255, 0}; 85: const RGB_INT magenta = {255, 0, 255}; 86: const RGB_INT cyan = {0, 255, 255}; 87: const RGB_INT red = {255, 0, 0}; 88: const RGB_INT green = {0, 255, 0}; 89: const RGB_INT blue = {0, 0, 255}; 90: const RGB_INT black = {0, 0, 0}; 91: 92: const RGB_INT gray = {128, 128, 128}; 93: const RGB_INT lt_yellow = {255, 255, 128}; 94: const RGB_INT lt_magenta = {255, 128, 255}; 95: const RGB_INT lt_cyan = {128, 255, 255}; 96: 97: const RGB_INT lt_red = {255, 128, 128}; 98: const RGB_INT lt_green = {128, 255, 128}; 99: const RGB_INT lt_blue = {128, 128, 255}; 100: 101: const RGB_INT dk_yellow = {128, 128, 0}; 102: const RGB_INT dk_magenta = {128, 0, 128}; 103: const RGB_INT dk_cyan = { 0, 128, 128}; 104: 105: const RGB_INT dk_red = {128, 0, 0}; 106: const RGB_INT dk_green = { 0, 128, 0}; 107: const RGB_INT dk_blue = { 0, 0, 128}; 108: 109: int cur_threash_val; /*for speed optimization of chain code recursion*/ 110: int back_ground; 111: 112: /*pointer to an array of pointers which point to the first nodes in each of 113: the chain codes.*/ 114: chainCode **chain_codes; 115: 116: 117: /*hold the points farthest away from each other for each object*/ 118: coord corner1[MAX_CHAINS], corner2[MAX_CHAINS]; 119: coord corner3[MAX_CHAINS], corner4[MAX_CHAINS]; 120: 121: /*use double buffered output and keep running enable idle callback to call 122: detect_corners() over and over simulating realtime response. 123: Is set to TRUE or FALSE.*/ 124: int is_buffered; 125: 126: /*used to determine if abstract lines should be drawn to the screen. 127: Set to <0 if no abstract lines are to be drawn. Otherwise 128: is set to the number of objects found (AKA number of chaincodes).*/ 129: int draw_abstract_lines; 130: 131: /*used to draw the single object most likely to be considered the 132: tic-tac-toe board. Should be <0 if this should not be drawn. 133: Should be equal to the chain-code number (from 0 to MAX_CHAINS - 1) 134: if it is to be drawn.*/ 135: int draw_abstract_board; 136: 137: 138: /**************Drawing funcitions************************************/ 139: /********************************************************************/ 140: 141: void setCPixel(int ix, int iy, RGB_INT color) 142: /*Same as setIPixel except that the last parameter is an RGB color*/ 143: { 144: float x = (ix*2.0)/HSIZE - 1.0; 145: float y = (iy*2.0)/VSIZE - 1.0; 146: 147: float red = (float)color.red/(float)MVAL; 148: float green = (float)color.green/(float)MVAL; 149: float blue = (float)color.blue/(float)MVAL; 150: 151: glColor3f(red, green, blue); 152: 153: glBegin(GL_POINTS); 154: glVertex2f (x, y); 155: glEnd(); 156: } 157: 158: void setCLines(int ix1, int iy1, int ix2, int iy2, RGB_INT color) 159: /*Similar as setIPixel except that this one draws a line between the first set 160: of points given and the second set in the RGB color specified*/ 161: { 162: float x1 = (ix1*2.0)/HSIZE - 1.0; 163: float y1 = (iy1*2.0)/VSIZE - 1.0; 164: float x2 = (ix2*2.0)/HSIZE - 1.0; 165: float y2 = (iy2*2.0)/VSIZE - 1.0; 166: 167: float red = (float)color.red/(float)MVAL; 168: float green = (float)color.green/(float)MVAL; 169: float blue = (float)color.blue/(float)MVAL; 170: 171: glColor3f(red, green, blue); 172: 173: glBegin(GL_LINES); 174: glVertex2f (x1, y1); 175: glVertex2f (x2, y2); 176: glEnd(); 177: } 178: 179: void setCRect(int ix1, int iy1, int ix2, int iy2, RGB_INT color) 180: /*Similar as setIPixel except that this one draws a line between the first set 181: of points given and the second set in the RGB color specified*/ 182: { 183: float x1 = (ix1*2.0)/HSIZE - 1.0; 184: float y1 = (iy1*2.0)/VSIZE - 1.0; 185: float x2 = (ix2*2.0)/HSIZE - 1.0; 186: float y2 = (iy2*2.0)/VSIZE - 1.0; 187: 188: float red = (float)color.red/(float)MVAL; 189: float green = (float)color.green/(float)MVAL; 190: float blue = (float)color.blue/(float)MVAL; 191: 192: glColor3f(red, green, blue); 193: 194: glBegin(GL_POLYGON); 195: glVertex2f (x1, y1); 196: glVertex2f (x1, y2); 197: glVertex2f (x2, y2); 198: glVertex2f (x2, y1); 199: glEnd(); 200: } 201: 202: void pxlcpy(PGMImage *dest, int dest_row, int dest_col, 203: PGMImage *src, int src_row, int src_col) 204: { 205: /*make sure values are within bounds*/ 206: if(dest_col > 0 && dest_col < (*dest).width 207: && dest_row > 0 && dest_row < (*dest).height 208: && src_col > 0 && src_col < (*src).width 209: && src_row > 0 && src_row < (*src).height) 210: { 211: (*dest).data[dest_row][dest_col].red = 212: (*src).data[src_row][src_col].red; 213: 214: (*dest).data[dest_row][dest_col].green = 215: (*src).data[src_row][src_col].green; 216: 217: (*dest).data[dest_row][dest_col].blue = 218: (*src).data[src_row][src_col].blue; 219: } 220: } 221: 222: 223: /**********************Support functions*******************************/ 224: /***********************************************************************/ 225: 226: int rgb_avg(RGB_INT cur_pxl) 227: { 228: /*convert each RGB to the average of the original*/ 229: return ((cur_pxl.red + cur_pxl.green + cur_pxl.blue) / 3); 230: } 231: 232: /*Returns average (with RGB avg) pixel value for the image passed in.*/ 233: int img_pxl_avg(PGMImage* img) 234: { 235: int i, j; /*loop counting*/ 236: int sum = 0; 237: 238: for(i = 0; i < (*img).height; i++)/*collumn*/ 239: for(j = 0; j < (*img).width; j++)/*row*/ 240: sum += rgb_avg((*img).data[i][j]); 241: 242: return (sum / ((*img).height * (*img).height)); 243: } 244: 245: /*1st: pixel one of type RGB_INT 246: 2nd: pixel one of type RGB_INT 247: 3rd: differnce allowed to be considered "equal" or close enough*/ 248: int pxlcmp (RGB_INT pxl1, RGB_INT pxl2, int range) 249: { 250: return ((abs((rgb_avg(pxl1) - rgb_avg(pxl2)))) < range); 251: } 252: 253: /* ================================================================= 254: * drawString - outputs a string of characters to the graphics port 255: * 256: * x, y: defines the starting location to draw the text 257: * note: this point is the lower left anchor of 258: * the first character - a character's decending 259: * portion would be drawn below this point. 260: * theFont: points to the glut font to be used 261: * theString: holds the string to be output -- up to 255 ch 262: * ----------------------------------------------------------------- */ 263: void drawString(int ix, int iy, void *theFont, char theString[256]) 264: { 265: float x = (ix*2.0)/HSIZE - 1.0; 266: float y = (iy*2.0)/VSIZE - 1.0; 267: int i; 268: glRasterPos2f(x, y); 269: for (i = 0; theString[i] != '\0'; i++) /* draw the chars one at a time */ 270: glutBitmapCharacter(theFont, theString[i]); 271: } 272: 273: /*return >0 if number of pixels is greater than img. pxl. avg., return <0 if 274: number of pixesl is less than img. pxl. avg. and return zero of equal*/ 275: int background(int treash_value, PGMImage* img) 276: { 277: int i, j; /*loop counting*/ 278: int pxl_less = 0, pxl_more = 0; 279: 280: for(i = 0; i < (*img).height; i++)/*collumn*/ 281: for(j = 0; j < (*img).width; j++)/*row*/ 282: { 283: if(rgb_avg((*img).data[i][j]) < treash_value) 284: pxl_less++; 285: 286: if(rgb_avg((*img).data[i][j]) > treash_value) 287: pxl_more++; 288: } 289: 290: if(pxl_less > pxl_more) 291: return -1; 292: else if(pxl_less < pxl_more) 293: return 1; 294: else 295: return 0; 296: } 297: 298: /******CHAIN CODE************************************************ 299: ****************************************************************/ 300: 301: int createNode(chainCode** first, chainCode** last, int new_x, int new_y, 302: int new_dir) 303: { 304: chainCode* temp; 305: 306: if(!(temp = (chain_t)malloc(sizeof(struct chainCode)))) 307: { 308: printf("error creating node\n"); 309: return FALSE; 310: } 311: 312: temp->next = NULL; 313: temp->code = new_dir; 314: temp->location.x = new_x; 315: temp->location.y = new_y; 316: temp->prev = *last; 317: if(*last) 318: (*last)->next = temp; /*now this is a good thing to set.*/ 319: *last = temp; /*advance one*/ 320: 321: if(*first == NULL) 322: *first = *last;/*set this for the first node in the list once*/ 323: 324: return TRUE; 325: } 326: 327: /*returns TRUE if the chain code loops around itself, otherwise false*/ 328: int checkCode(chainCode* first, int x_current, int y_current, 329: int x_check, int y_check, int dir) 330: { 331: struct chainCode* currentNode = first; 332: 333: while(currentNode && currentNode->next) 334: { 335: /*check two consedutive points*/ 336: if((currentNode->location.x == x_current) && 337: (currentNode->location.y == y_current) && 338: (currentNode->code == dir) && 339: (currentNode->next->location.x == x_check) && 340: (currentNode->next->location.y == y_check) && 341: (currentNode->next->next)) /*don't count the new node already*/ 342: { 343: return TRUE; /*The chain code end looped around*/ 344: } 345: else 346: currentNode = currentNode->next; 347: } 348: return FALSE; 349: } 350: 351: /* 352: Preconditions: 353: 1st parameter: integer value containing the current x coordinate. 354: 2nd parameter: integer value containing the current y coordinate. 355: 3rd parameter: pointer to the pgm image being used. 356: Postconditions: return true if pixel is deemed boarder of object, false 357: otherwise.*/ 358: int checkThings(int x_check, int y_check, PGMImage *img) 359: { 360: 361: /*check for being in bounds*/ 362: if((x_check < 0) || (y_check < 0) || (x_check >= (*img).width) || 363: (y_check >= (*img).height)) 364: return FALSE; 365: 366: /*tests if the next pixel is greater than the threashing value. This 367: value (cur_threash_val) should be set to the return value of 368: img_pxl_avg(). Also, checks if the pixel is considered in the background 369: or forground agains the value in back_ground. If so, return true to 370: indicate create new node.*/ 371: if((rgb_avg((*img).data[y_check][x_check]) > cur_threash_val) 372: && (back_ground < 0)) 373: return TRUE; 374: 375: /*same thing as above, just for things less than, rathur than greater than*/ 376: if((rgb_avg((*img).data[y_check][x_check]) < cur_threash_val) 377: && (back_ground > 0)) 378: return TRUE; 379: 380: return FALSE; 381: } 382: 383: coord neighborhood_point(int x_coord, int y_coord, int dir) 384: { 385: coord temp = {x_coord, y_coord}; 386: 387: if(dir == EAST) 388: temp.x++; 389: else if(dir == NORTHEAST) 390: { 391: temp.x++; 392: temp.y++; 393: } 394: else if(dir == NORTH) 395: temp.y++; 396: else if(dir == NORTHWEST) 397: { 398: temp.x--; 399: temp.y++; 400: } 401: else if(dir == WEST) 402: temp.x--; 403: else if(dir == SOUTHWEST) 404: { 405: temp.x--; 406: temp.y--; 407: } 408: else if(dir == SOUTH) 409: temp.y--; 410: else if(dir == SOUTHEAST) 411: { 412: temp.x++; 413: temp.y--; 414: } 415: 416: return temp; 417: } 418: 419: /*determines the chain code for a starting pixel*/ 420: /*this chain code uses 8 connectivity*/ 421: /*Note: There are many different coordinate systems at work in this function.*/ 422: /*The (*img).data[][] are column major and have are therefore [y][x] or [j][i]*//* This is also asuming (0,0) is bottom left, where the other code assumes 423: /* top left is (0, 0). If it looks strange, look harder!!!*/ 424: /*Preconditions: 425: 1st parameter: pointer to the pgm image being used. 426: 2nd parameter: integer value containing the current x coordinate. 427: 3rd parameter: integer value containing the current y coordinate. 428: 4th parameter: integer value containing the current direction code. 429: 5th parameter: pointer to the last node of the chain code. 430: 6th parameter: pointer to the first node of the chain code.*/ 431: /*This function assumes the (0, 0) point is in the lower left*/ 432: 433: int chaincode(PGMImage *img, int x, int y, int dir, 434: struct chainCode** last, struct chainCode** first) 435: { 436: int i; /*loop counting*/ 437: int next /*next direction to check*/, finished /*finished flag*/; 438: coord next_point_to_check; /*next_point_to_check What else?*/ 439: 440: next = (dir + 5) % 8; /*better initail choice?*/ 441: 442: /*printf("next: %d x: %d y: %d\n", next, x, y);*/ 443: for(i = 0; i < 8; i++) 444: { 445: /*determine the next point to check*/ 446: next_point_to_check = neighborhood_point(x, y, next); 447: 448: if(checkThings(next_point_to_check.x, next_point_to_check.y, img)) 449: { 450: /*setCPixel(next_point_to_check.x, next_point_to_check.y, blue);*/ 451: 452: /*create the new node to go last in the chain*/ 453: createNode(first, last, next_point_to_check.x, 454: next_point_to_check.y, next); 455: 456: /*if the next chaincode function in the recursion or the current state 457: is the final state of the the chain code; recursively returns DONE 458: back through all the levels to stop the chain code.*/ 459: if(checkCode(*first, x, y, next_point_to_check.x, 460: next_point_to_check.y, dir)) 461: { 462: return NONE; 463: } 464: 465: if(NONE == chaincode(img, next_point_to_check.x, 466: next_point_to_check.y, next, last, first)) 467: return NONE; 468: } 469: 470: /*set next for next iteration*/ 471: next = (next + 1) % 8; 472: } 473: return FALSE; 474: } 475: 476: /*returns true if the point is already in one of the chains, false otherwise*/ 477: int alreadyFound(int initThreashFlag, int i, int j, chainCode** found) 478: { 479: int k; /*loop counting*/ 480: chainCode* temp; /*temporary node pointer*/ 481: 482: /*while there are codes to check...*/ 483: for(k = 0; (temp = (found[k])) && k < MAX_NUMBER_OF_CHAINS; k++) 484: { 485: while(temp) /*...check them.*/ 486: { 487: /*if point has already been found*/ 488: if((temp->location.x == i) && (temp->location.y == j)) 489: { 490: return TRUE; 491: break; 492: } 493: temp = temp->next; 494: } 495: } 496: 497: return FALSE; 498: } 499: 500: /*saves a chain code to file. Was usefull during debuging now is just 501: a cool thing to leave in*/ 502: /*1st: pointer to beginning of a chaincode 503: 2nd: the index of the chain code for destiqushing the 'chain' files*/ 504: void saveChainCode(chainCode* saveChain, int count) 505: { 506: struct chainCode* temp = saveChain; 507: char filename[12]; 508: FILE* outfile; 509: sprintf(filename, "chain%d", count); 510: outfile = fopen(filename, "w"); /*output file for chaincode*/ 511: /*printf("Writing chain code to file %s.\n", filename);*/ 512: 513: while(temp) 514: { 515: 516: fprintf(outfile, "%d %d %d\n",temp->location.x,temp->location.y, 517: temp->code); 518: temp = temp->next; 519: } 520: fclose(outfile); 521: } 522: 523: chainCode** showChain(PGMImage *original) 524: { 525: int i, j; /*loop counting*/ 526: int count = 0; /*array index holder*/ 527: int initThreashFlag = img_pxl_avg(original); 528: /*beginning points to an array of pointers. Each pointer in the array is 529: a pointer to the first node of MAX_NUMBER_OF_CHAINS number of chain 530: codes*/ 531: struct chainCode** beginning, *chain = NULL; 532: 533: /*allocate an array of pointers to chainCodes, then initalize their 534: values to NULL*/ 535: beginning = (chainCode**) 536: malloc(MAX_NUMBER_OF_CHAINS * sizeof(chainCode*)); 537: memset(beginning, 0, MAX_NUMBER_OF_CHAINS * sizeof(chainCode*)); 538: 539: cur_threash_val = initThreashFlag; /*set global variables*/ 540: back_ground = background(initThreashFlag, original); 541: 542: /*search image until a pixel is found with threasholded value of the 543: object. i & j will then contain the starting coordinate.*/ 544: for(i = 0; i < (*original).width; i++) /*x direction*/ 545: { 546: for(j = (*original).height - 1; j >= 0; j--) /*y direction*/ 547: { 548: /*skip to the next iteration if pixel isn't "good" enough*/ 549: if(!checkThings(i, j, original)) 550: continue; 551: 552: /*skip to the next iteration, which will be at the top of the next 553: collumn. This is true when the pixel has already been found in 554: one of the chain codes.*/ 555: else if(alreadyFound(initThreashFlag, i, j, beginning)) 556: break; 557: 558: else 559: { 560: /*printf("chaincode: %d\n", count);*/ 561: /*printf("The starting coordinate is (x, y): (%d, %d)\n", i, j);*/ 562: chain = NULL; /*reset this to seperate the chaincodes*/ 563: 564: /*find the chaincode for the current starting pixel*/ 565: chaincode(original, i, j, SOUTHEAST, &chain, &beginning[count]); 566: if(beginning[count] != NULL) /*avoid writing zero length chains*/ 567: { 568: saveChainCode(beginning[count], count); 569: count++; /*advance the beginning counter*/ 570: } 571: 572: /*force end of loops, leaving code when finished looping 573: to still execute*/ 574: if(count >= MAX_NUMBER_OF_CHAINS) 575: i = (*original).width; 576: 577: break; /*check the next collumn*/ 578: } 579: } 580: } 581: printf("\nDone finding chain code(s). %d were found.\n", count); 582: return beginning; 583: } 584: 585: /*frees the memory that the chain code(s) are using*/ 586: /*aogp = address of global pointer*/ 587: /*upon success the pointer whose address is passed in is set to NULL.*/ 588: void free_chaincode(chainCode*** aogp) 589: { 590: int i; /*loop counting*/ 591: chainCode* temp, *to_free; 592: /*create a temporay pointer, since three asteriks is crazy to use...*/ 593: chainCode** pointer = *aogp; 594: 595: /*for each chaincode, free all of the nodes.*/ 596: for(i = 0; pointer && pointer[i] && i < MAX_CHAINS; i++) 597: { 598: temp = pointer[i]; 599: while(temp) /*for each node*/ 600: { 601: to_free = temp; 602: temp = temp->next; 603: free(to_free); 604: } 605: } 606: 607: free(pointer); /*free the array of pointers to chaincodes*/ 608: 609: *aogp = NULL; /*set global to NULL indicating there isn't a chaincode*/ 610: } 611: 612: /*****BOUNDRY BOX********************************************************** 613: **********************************************************************/ 614: 615: /*takes two coordinates as x and y pairs and returns the distence betweem them 616: as a decimal*/ 617: float findDist(int x1, int y1, int x2, int y2) 618: { 619: return sqrt(((x1 - x2) * (x1 - x2)) + ((y1 - y2) * (y1 - y2))); 620: } 621: 622: /*1st param: The array of coords that the first point of the major axis is 623: returned in. 624: 2nd: The array of coords that the second point of the major axis is 625: returned in. 626: 3rd: The array of chain codes that are searched through to find the major 627: axes.*/ 628: /* Note: the ending condition is when a NULL chainCodes value is reached. 629: Thusly, it works like a string requiring it to have the last legal value 630: followed by a null value.*/ 631: void findFirstTwoCorners(coord corner1[], coord corner2[], 632: chainCode** chainCodes) 633: { 634: int i; /*loop counting*/ 635: chainCode *temp, *search; 636: double max_dist, test_dist; 637: 638: /*printf("\nFinding first 2 corners.\n");*/ 639: 640: /*as long as there are codes to check, keep checking. Note: the ending 641: condition is when a NULL chainCodes value is reached. Thusly, it works 642: like a string requiring it to have the last legal value followed by a 643: null value.*/ 644: for(i = 0; (temp = chainCodes[i]) && (i < MAX_CHAINS); i++) 645: { 646: max_dist = 0.0; /*reset this for each iteration*/ 647: 648: /*printf("checking list: %d\n", i);*/ 649: 650: while(temp) /*while there are still nodes to check in the chain*/ 651: { 652: search = temp; /*set the faster moving search pointer to temp, 653: this increases the effiecency a lot compared to 654: setting it equal to the first node..*/ 655: while(search) 656: { 657: /*setCPixel(temp->location.x, temp->location.y, green);*/ 658: 659: /*determine if found a new maximum distance between two locations*/ 660: if((test_dist = findDist(search->location.x, search->location.y, 661: temp->location.x, temp->location.y)) > max_dist) 662: { 663: max_dist = test_dist; 664: corner1[i].x = temp->location.x; 665: corner1[i].y = temp->location.y; 666: corner2[i].x = search->location.x; 667: corner2[i].y = search->location.y; 668: } 669: search = search->next; 670: } 671: temp = temp->next; 672: } 673: /*printf("point1: %d %d\n", max1[i].x, max1[i].y); 674: printf("point2: %d %d\n", max2[i].x, max2[i].y);*/ 675: } 676: } 677: 678: /*1st param: Array of coords for the first corner of each chain code. 679: 2nd param: Array of coords for the second corner of each chain code. 680: The first two parameters should equal the first two parameters "returned" 681: from the findFirstTwoCorners() function. 682: 3rd: Array of coords "returned" with the third corners. 683: 4th: Array of coords "returned" with the fourth corners. 684: 5th: Pointer pointing to the array of chaincode pointers, obtained from 685: showChain().*/ 686: void findSecondTwoCorners(coord corner1[], coord corner2[], coord corner3[], 687: coord corner4[], chainCode** chain_code_array) 688: { 689: int i; /*loop counting*/ 690: chainCode* temp; 691: float temp_dist1, temp_dist2; /*distance between point and each corner*/ 692: coord canidate_coord1, temp_coord; 693: float canidate_dist1, max_dist; 694: int corner_count; 695: 696: /*printf("\nFinding last 2 corners.\n\n");*/ 697: 698: /*for each chain code find the corners*/ 699: for(i = 0; chain_code_array[i] && i < MAX_CHAINS; i++) 700: { 701: temp = chain_code_array[i]; 702: 703: /*reset these for the next chain code*/ 704: max_dist = 0.0; 705: corner_count = 1; 706: 707: while(temp) /*while there are nodes in the chain code to check*/ 708: { 709: /*setCPixel(temp->location.x, temp->location.y, color1);*/ 710: 711: /*determine the first canidate coord for corner 3/4*/ 712: if(((temp->location.x == corner1[i].x) 713: && (temp->location.y == corner1[i].y)) 714: || ((temp->location.x == corner2[i].x) 715: && (temp->location.y == corner2[i].y))) 716: { 717: /*if this corner found is the first of the two allready known 718: corners, then set the first canidate coord data and reset data 719: to find the next canidate corner point*/ 720: if(corner_count == 1) 721: { 722: canidate_coord1.x = temp_coord.x; 723: canidate_coord1.y = temp_coord.y; 724: canidate_dist1 = max_dist; 725: 726: corner_count = 2; /*set for next corner*/ 727: max_dist = 0.0; 728: } 729: else if(corner_count == 2) 730: { 731: /*the second canidate is always a corner*/ 732: corner4[i].x = temp_coord.x; 733: corner4[i].y = temp_coord.y; 734: 735: max_dist = 0.0; /*set for next corner canidate*/ 736: } 737: } 738: 739: /*calculate the distance between the current point being checked and 740: each corner point*/ 741: temp_dist1 = findDist(corner1[i].x, corner1[i].y, temp->location.x, 742: temp->location.y); 743: temp_dist2 = findDist(corner2[i].x, corner2[i].y, temp->location.x, 744: temp->location.y); 745: 746: /*if the current point is the furthest away sofar, store this point 747: untill it is overridden or becomes a canidate point*/ 748: if((temp_dist1 + temp_dist2) > max_dist) 749: { 750: temp_coord.x = temp->location.x; 751: temp_coord.y = temp->location.y; 752: 753: max_dist = (temp_dist1 + temp_dist2); 754: } 755: 756: temp = temp->next; /*advance*/ 757: } 758: 759: /*from the three canidate coords find the two real corners.*/ 760: /*the second canidate will always be a corner, must test 1 vs 3, where 761: three is in the variables temp_coord and max_dist.*/ 762: if(canidate_dist1 > max_dist) /*first canidate*/ 763: { 764: corner3[i].x = canidate_coord1.x; 765: corner3[i].y = canidate_coord1.y; 766: } 767: else /*third canidate*/ 768: { 769: corner3[i].x = temp_coord.x; 770: corner3[i].y = temp_coord.y; 771: } 772: /*printf("corner3: (%d, %d) corner4: (%d, %d)\n", corner3[i].x, 773: corner3[i].y, corner4[i].x, corner4[i].y);*/ 774: } 775: } 776: 777: /*takes a pointer to an image, and a pointer pointing to an array of 778: chain codes pointers, here each chainCode pointer needs to be accessed 779: by calculating the memory address.*/ 780: void showBound(PGMImage *original, chainCode** chainCodes) 781: { 782: int i; 783: 784: /*find the first two corners. they will be across a diagnal.*/ 785: findFirstTwoCorners(corner1, corner2, chainCodes); 786: /*find the second two corners. they will be across a diagnal too.*/ 787: findSecondTwoCorners(corner1, corner2, corner3, corner4, chainCodes); 788: /* 789: for(i = 0; chainCodes[i] && i < MAX_CHAINS; i++) 790: { 791: setCLines(corner1[i].x, corner1[i].y, corner3[i].x, corner3[i].y,yellow); 792: setCLines(corner4[i].x, corner4[i].y, corner2[i].x, corner2[i].y,yellow); 793: setCLines(corner3[i].x, corner3[i].y, corner2[i].x, corner2[i].y,yellow); 794: setCLines(corner4[i].x, corner4[i].y, corner1[i].x, corner1[i].y,yellow); 795: setCPixel(corner1[i].x, corner1[i].y, magenta); 796: setCPixel(corner2[i].x, corner2[i].y, magenta); 797: setCPixel(corner3[i].x, corner3[i].y, magenta); 798: setCPixel(corner4[i].x, corner4[i].y, magenta); 799: }*/ 800: } 801: 802: /**********************File I/O functions*******************************/ 803: /***********************************************************************/ 804: 805: /*Gets an ascii color pgm image file (type P3).*/ 806: void getPGMfile (char filename[], PGMImage *img) 807: { 808: FILE *in_file; 809: char ch; 810: int row, col, type; 811: 812: in_file = fopen(filename, "r"); 813: if (in_file == NULL) 814: { 815: fprintf(stderr, "Error: Unable to open file %s\n\n", filename); 816: exit(8); 817: } 818: 819: printf("\nReading image file: %s\n", filename); 820: 821: /*determine pgm image type (only type three can be used)*/ 822: ch = getc(in_file); 823: if(ch != 'P') 824: { 825: printf("ERROR(1): Not valid pgm/ppm file type\n"); 826: exit(1); 827: } 828: ch = getc(in_file); 829: /*convert the one digit integer character to integer(48 == '0')*/ 830: type = ch - 48; 831: if((type != 3) && (type != 6)) 832: { 833: printf("ERROR(2): Not valid pgm/ppm file type\n"); 834: exit(1); 835: } 836: 837: while(getc(in_file) != '\n'); /* skip to end of line*/ 838: 839: while (getc(in_file) == '#'); /* skip comments lines */ 840: { 841: while (getc(in_file) != '\n'); /* skip to end of line */ 842: } 843: 844: fseek(in_file, -1, SEEK_CUR); /* backup one character */ 845: 846: fscanf(in_file,"%d", &((*img).width)); 847: fscanf(in_file,"%d", &((*img).height)); 848: fscanf(in_file,"%d", &((*img).maxVal)); 849: 850: printf("\n width = %d",(*img).width); 851: printf("\n height = %d",(*img).height); 852: printf("\n maxVal = %d",(*img).maxVal); 853: printf("\n"); 854: 855: if (((*img).width > MAX) || ((*img).height > MAX)) 856: { 857: printf("\n\n***ERROR - image too big for current image structure***\n\n"); 858: exit(1); 859: } 860: 861: if(type == 3) /*uncompressed ascii file*/ 862: { 863: for (row=(*img).height-1; row>=0; row--) 864: for (col=0; col<(*img).width; col++) 865: { 866: fscanf(in_file,"%d", &((*img).data[row][col].red)); 867: fscanf(in_file,"%d", &((*img).data[row][col].green)); 868: fscanf(in_file,"%d", &((*img).data[row][col].blue)); 869: } 870: } 871: else if(type == 6) /*compressed file*/ 872: { 873: while(getc(in_file) != '\n'); /*skip to end of line*/ 874: 875: for (row=(*img).height-1; row>=0; row--) 876: for (col=0; col<(*img).width; col++) 877: { 878: (*img).data[row][col].red = getc(in_file); 879: (*img).data[row][col].green = getc(in_file); 880: (*img).data[row][col].blue = getc(in_file); 881: } 882: } 883: 884: fclose(in_file); 885: printf("\nDone reading file.\n"); 886: } 887: 888: 889: void save(PGMImage *img) 890: { 891: int i, j, nr, nc, k; 892: int red, green, blue; 893: FILE *iop; 894: 895: nr = img->height; 896: nc = img->width; 897: 898: iop = fopen("image1.pgm", "w"); 899: fprintf(iop, "P3\n"); 900: fprintf(iop, "%d %d\n", nc, nr); 901: fprintf(iop, "255\n"); 902: 903: k = 1; 904: for(i = nr - 1; i >= 0; i--) 905: { 906: for(j = 0; j < nc; j++) 907: { 908: red = img->data[i][j].red; 909: green = img->data[i][j].green; 910: blue = img->data[i][j].blue; 911: if(red < 0) 912: { 913: printf("IMG_WRITE: Found value %d at row %d col %d\n", red, i, j); 914: printf(" Setting red to zero\n"); 915: red = 0; 916: } 917: if(green < 0) 918: { 919: printf("IMG_WRITE: Found value %d at row %d col %d\n", green,i, j); 920: printf(" Setting green to zero\n"); 921: green = 0; 922: } 923: if(blue < 0) 924: { 925: printf("IMG_WRITE: Found value %d at row %d col %d\n", blue, i, j); 926: printf(" Setting green to zero\n"); 927: blue = 0; 928: } 929: if(red > 255) 930: { 931: printf("IMG_WRITE: Found value %d at row %d col %d\n", red, i, j); 932: printf(" Setting red to 255\n"); 933: red = 255; 934: } 935: if(green > 255) 936: { 937: printf("IMG_WRITE: Found value %d at row %d col %d\n", green,i, j); 938: printf(" Setting green to 255\n"); 939: green = 255; 940: } 941: if(blue > 255) 942: { 943: printf("IMG_WRITE: Found value %d at row %d col %d\n", blue, i, j); 944: printf(" Setting blue to 255\n"); 945: blue = 255; 946: } 947: 948: if(k % 10) 949: { 950: fprintf(iop, "%d ", red); 951: fprintf(iop, "%d ", green); 952: fprintf(iop, "%d ", blue); 953: } 954: else /*for newline*/ 955: { 956: fprintf(iop, "%d\n", red); 957: fprintf(iop, "%d\n", green); 958: fprintf(iop, "%d\n", blue); 959: } 960: k++; 961: } 962: } 963: fprintf(iop, "\n"); 964: fclose(iop); 965: } 966: 967: 968: /****Calculation & drawing functions of the image translations********** 969: ***********************************************************************/ 970: 971: coord find_dividers(coord point1, coord point2, float t) 972: { 973: coord temp; 974: 975: temp.x = (((1.0 - t) * point1.x) + (t * point2.x)); 976: temp.y = (((1.0 - t) * point1.y) + (t * point2.y)); 977: 978: return temp; 979: } 980: 981: void showColor (PGMImage *img) 982: { 983: int i, j; /*loop counting: i = y, j = x*/ 984: float t = 1.0 / 3.0; 985: coord hor_top1, hor_top2; 986: coord hor_bot1, hor_bot2; 987: coord ver_rgt1, ver_rgt2; 988: coord ver_lft1, ver_lft2; 989: 990: GLubyte checkImage[(*img).height][(*img).width][3]; 991: 992: for(i = 0; i < (*img).height; i++) 993: { 994: for(j = 0; j < (*img).width; j++) 995: { 996: checkImage[i][j][0] = (GLubyte) (*img).data[i][j].red; 997: checkImage[i][j][1] = (GLubyte) (*img).data[i][j].green; 998: checkImage[i][j][2] = (GLubyte) (*img).data[i][j].blue; 999: } 1000: } 1001: 1002: glPixelStorei(GL_UNPACK_ALIGNMENT, 1); 1003: glRasterPos2f(-1, -1); 1004: glDrawPixels((*img).width, (*img).height, GL_RGB, 1005: GL_UNSIGNED_BYTE, checkImage); 1006: 1007: /*first check for non-null pointer, next check for dereferenced pointers 1008: in the array of head pointers and lastly make sure things stay in 1009: bound of the max incase all MAX_CHAINS number of chains are used.*/ 1010: /*draw_abstract_lines is the global that holds the number of "objects" 1011: to draw abstract information for*/ 1012: for(i = 0; i < draw_abstract_lines && i < MAX_CHAINS; i++) 1013: { 1014: setCLines(corner1[i].x, corner1[i].y, corner3[i].x, corner3[i].y,yellow); 1015: setCLines(corner4[i].x, corner4[i].y, corner2[i].x, corner2[i].y,yellow); 1016: setCLines(corner3[i].x, corner3[i].y, corner2[i].x, corner2[i].y,yellow); 1017: setCLines(corner4[i].x, corner4[i].y, corner1[i].x, corner1[i].y,yellow); 1018: setCPixel(corner1[i].x, corner1[i].y, magenta); 1019: setCPixel(corner2[i].x, corner2[i].y, magenta); 1020: setCPixel(corner3[i].x, corner3[i].y, magenta); 1021: setCPixel(corner4[i].x, corner4[i].y, magenta); 1022: } 1023: 1024: if(draw_abstract_board >= 0 && draw_abstract_board < MAX_CHAINS) 1025: { 1026: /*This code should draw the bounding box*/ 1027: /*setCLines(corner1[draw_abstract_board].x, 1028: corner1[draw_abstract_board].y, 1029: corner3[draw_abstract_board].x, 1030: corner3[draw_abstract_board].y,blue); 1031: setCLines(corner4[draw_abstract_board].x, 1032: corner4[draw_abstract_board].y, 1033: corner2[draw_abstract_board].x, 1034: corner2[draw_abstract_board].y,blue); 1035: setCLines(corner3[draw_abstract_board].x, 1036: corner3[draw_abstract_board].y, 1037: corner2[draw_abstract_board].x, 1038: corner2[draw_abstract_board].y,blue); 1039: setCLines(corner4[draw_abstract_board].x, 1040: corner4[draw_abstract_board].y, 1041: corner1[draw_abstract_board].x, 1042: corner1[draw_abstract_board].y,blue); 1043: setCPixel(corner1[draw_abstract_board].x, 1044: corner1[draw_abstract_board].y, red); 1045: setCPixel(corner2[draw_abstract_board].x, 1046: corner2[draw_abstract_board].y, red); 1047: setCPixel(corner3[draw_abstract_board].x, 1048: corner3[draw_abstract_board].y, red); 1049: setCPixel(corner4[draw_abstract_board].x, 1050: corner4[draw_abstract_board].y, red); 1051: */ 1052: 1053: /*this code calculates where the dividers should be*/ 1054: hor_top1 = find_dividers(corner2[draw_abstract_board], 1055: corner3[draw_abstract_board], ONE_THIRD); 1056: hor_top2 = find_dividers(corner4[draw_abstract_board], 1057: corner1[draw_abstract_board], ONE_THIRD); 1058: 1059: hor_bot1 = find_dividers(corner3[draw_abstract_board], 1060: corner2[draw_abstract_board], ONE_THIRD); 1061: hor_bot2 = find_dividers(corner1[draw_abstract_board], 1062: corner4[draw_abstract_board], ONE_THIRD); 1063: 1064: ver_rgt1 = find_dividers(corner4[draw_abstract_board], 1065: corner2[draw_abstract_board], ONE_THIRD); 1066: ver_rgt2 = find_dividers(corner1[draw_abstract_board], 1067: corner3[draw_abstract_board], ONE_THIRD); 1068: 1069: ver_lft1 = find_dividers(corner2[draw_abstract_board], 1070: corner4[draw_abstract_board], ONE_THIRD); 1071: ver_lft2 = find_dividers(corner3[draw_abstract_board], 1072: corner1[draw_abstract_board], ONE_THIRD); 1073: 1074: /*this code draws over where the dividers should be.*/ 1075: setCLines(hor_top1.x, hor_top1.y, hor_top2.x, hor_top2.y, cyan); 1076: setCLines(hor_bot1.x, hor_bot1.y, hor_bot2.x, hor_bot2.y, cyan); 1077: setCLines(ver_rgt1.x, ver_rgt1.y, ver_rgt2.x, ver_rgt2.y, cyan); 1078: setCLines(ver_lft1.x, ver_lft1.y, ver_lft2.x, ver_lft2.y, cyan); 1079: } 1080: glFlush(); 1081: } 1082: 1083: void camera_correction(PGMImage* new_img, PGMImage* org_img) 1084: { 1085: int row, col, img_row, img_col; /*loop counting*/ 1086: 1087: /*camera parameters*/ 1088: float height = 30; /*height of camera in cm*/ 1089: float gamma = 0, theta = .698; /*camera angles = 40 degrees in rad*/ 1090: float aperture = 1.1968, alpha = .598; /*aperature = 2 * alpha*/ 1091: 1092: /*temporary varaibles*/ 1093: float angular_height_corr; 1094: float angular_side_corr; 1095: int x_coord, y_coord; 1096: 1097: memset(new_img, 0, sizeof(PGMImage)); 1098: (*new_img).height = (*org_img).height; 1099: (*new_img).width = (*org_img).width; 1100: (*new_img).maxVal = (*org_img).maxVal; 1101: 1102: for (row=(*org_img).height-1; row>=0; row--) 1103: for (col=0; col<(*org_img).width; col++) 1104: { 1105: /*img_row -= (*org_img).height / 2; 1106: img_col = col - (*org_img).width / 2;*/ 1107: 1108: angular_height_corr = 1109: (theta - alpha) + col * (aperture / ((float)((*org_img).width) - 1)); 1110: angular_side_corr = 1111: (gamma - alpha) + row * (aperture / ((float)((*org_img).height) - 1)); 1112: 1113: angular_height_corr /= 2; 1114: /*angular_side_corr /= 2;*/ 1115: /*height *= 2;*/ 1116: height = 150; 1117: 1118: x_coord = (int) 1119: (height * (1 / tan(angular_height_corr)) * cos(angular_side_corr)); 1120: y_coord = (int) 1121: (height * (1 / tan(angular_height_corr)) * sin(angular_side_corr)); 1122: 1123: /*x_coord += (*org_img).width / 2;*/ 1124: y_coord += (*org_img).height / 2; 1125: 1126: /*printf("org: (%d, %d) new: (%d, %d)\n\n", row, col, y_coord, x_coord);*/ 1127: 1128: pxlcpy(new_img, y_coord, x_coord, org_img, row, col); 1129: } 1130: } 1131: 1132: void new_corr(PGMImage* new_img, PGMImage* org_img) 1133: { 1134: /*i and j are the left half, k and l are the right half*/ 1135: float i, k; /*loop counting*/ 1136: int j, l, row; /*loop counting*/ 1137: int old_i, old_k; 1138: 1139: float ins_s = 2; /*insert constant starting value*/ 1140: float ins_k = ins_s; /*insert constant*/ 1141: 1142: /*The halfway marks in the width.*/ 1143: int mid_width_left = ((*new_img).width / 2) - 1; 1144: int mid_width_right = ((*new_img).width / 2); 1145: 1146: /*just to be thourough clear the memory and reset maxes*/ 1147: memset(new_img, 0, sizeof(PGMImage)); 1148: (*new_img).height = (*org_img).height; 1149: (*new_img).width = (*org_img).width; 1150: (*new_img).maxVal = (*org_img).maxVal; 1151: 1152: /*Loop through each row from top to bottom...*/ 1153: for(row = ((*new_img).height - 1); row >= 0; row--) 1154: { 1155: /*...reset moire interference removal counter...*/ 1156: old_i = ((*new_img).width / 2) - 1; 1157: old_k = ((*new_img).width / 2); 1158: 1159: /*...so each half is ajusted to remove perspective effect...*/ 1160: for(i = j = mid_width_left, k = l = mid_width_right 1161: ; i >= 0, j >= 0, k < (*new_img).width, l < (*new_img).width 1162: ; i -= ins_k, j--, k += ins_k, l++) 1163: { 1164: for(;old_i >= (int)i; old_i--) /*...in the left half...*/ 1165: pxlcpy(new_img, row, old_i, org_img, row, j); 1166: for(;old_k <= (int)k; old_k++) /*...in the right half.*/ 1167: pxlcpy(new_img, row, old_k, org_img, row, l); 1168: } 1169: /*Move the new image x_coord pixel counter to next new image pixel*/ 1170: ins_k -= ((ins_s - 1.0) / (*new_img).height); 1171: } 1172: } 1173: 1174: void color_to_gray(PGMImage* new_img, PGMImage* org_img) 1175: { 1176: int row, col; /*loop counting*/ 1177: RGB_INT cur_pxl; /*current pixel*/ 1178: 1179: (*new_img).height = (*org_img).height; 1180: (*new_img).width = (*org_img).width; 1181: (*new_img).maxVal = (*org_img).maxVal; 1182: 1183: /*Starting with the top row...*/ 1184: for(row = (*new_img).height - 1; row >= 0; row--) 1185: for(col = 0; col < (*new_img).width - 1; col++) 1186: { 1187: cur_pxl = (*org_img).data[row][col]; /*more readable*/ 1188: 1189: /*convert each RGB to the average of the original*/ 1190: (*new_img).data[row][col].red = rgb_avg(cur_pxl); 1191: (*new_img).data[row][col].green = rgb_avg(cur_pxl); 1192: (*new_img).data[row][col].blue = rgb_avg(cur_pxl); 1193: } 1194: } 1195: 1196: void moravec(PGMImage* new_img, PGMImage* org_img) 1197: { 1198: int row, col; /*loop counting*/ 1199: int i, j, k, l; /*Sanka, Hlavac & Boyle; p. 97 f. 4.73*/ 1200: int running_sum; 1201: float K = .5; /*.125 according to org. formula, but .5 is brighter*/ 1202: int max_val = 0, row_max, col_max; /* max porportion value in image*/ 1203: 1204: memset(new_img, 0, sizeof(PGMImage)); 1205: (*new_img).height = (*org_img).height; 1206: (*new_img).width = (*org_img).width; 1207: (*new_img).maxVal = (*org_img).maxVal; 1208: 1209: /*starting at the top row*/ 1210: for(row = (*new_img).height - 1 - 1; row > 0; row--) 1211: for(col = 1; col < (*new_img).width - 1; col++) /*left col start*/ 1212: { 1213: i = row; 1214: j = col; 1215: running_sum = 0; 1216: 1217: /*Sanka, Hlavac & Boyle; p. 97 f. 4.73*/ 1218: for(k = i - 1; k <= i + 1; k++) /*row*/ 1219: for(l = j - 1; l <= j + 1; l++) /*column*/ 1220: running_sum += abs(rgb_avg((*org_img).data[k][l]) - 1221: rgb_avg((*org_img).data[i][j])); 1222: 1223: /*assign the new pixel value*/ 1224: (*new_img).data[row][col].red = (int)(K * running_sum); 1225: (*new_img).data[row][col].green = (int)(K * running_sum); 1226: (*new_img).data[row][col].blue = (int)(K * running_sum); 1227: } 1228: } 1229: 1230: /*returns the number of objects found*/ 1231: int detect_corners(PGMImage* org_img) 1232: { 1233: int i; /*temporarily holds number of chaincodes*/ 1234: 1235: glPointSize(4); /*make points more visible, if desired*/ 1236: glLineWidth(4); 1237: 1238: /*if there is a chaincode already in memory, free it before a new one 1239: is created.*/ 1240: free_chaincode(&chain_codes); 1241: 1242: chain_codes = showChain(org_img); 1243: showBound(org_img, chain_codes); 1244: 1245: /*when this stops, i holds the number of chaincodes*/ 1246: for(i = 0; (i < MAX_CHAINS) && chain_codes[i]; i++); 1247: 1248: return i; 1249: } 1250: 1251: /*determines which object is the game board and determine moves made in 1252: the game.*/ 1253: /*takes the number of object corners (equal to # of chaincodes) as param.*/ 1254: int detect_game(int num_of_corners) 1255: { 1256: float length2to4[MAX_CHAINS], length1to3[MAX_CHAINS]; /*side pairs*/ 1257: float length1to4[MAX_CHAINS], length2to3[MAX_CHAINS]; /*side pairs*/ 1258: 1259: float error24to13[MAX_CHAINS], error14to23[MAX_CHAINS]; 1260: 1261: int i, k; /*loop counting*/ 1262: float denom_check; /*make sure denominators are not zero*/ 1263: chainCode* temp; 1264: 1265: /*the most likely object (0 to num_of_corners) that is to be considered 1266: as the board. The float is the error associated with this object.*/ 1267: int most_likely = -1; 1268: float ml_error = FLT_MAX; /*just to make sure*/ 1269: 1270: for(i = 0; i < num_of_corners; i++) 1271: { 1272: /*count the number of nodes in the chaincode. Unless the size is 1273: considered long enough, skip it and move on*/ 1274: for(temp = chain_codes[i], k = 1; temp; k++, temp = temp->next); 1275: if(k < DECENT_SIZED_OBJECT) 1276: continue; 1277: 1278: /*since points 1 & 2 are at a diagnal, and 3 & 4 are at a diagnol, 1279: then the dist between 2 and 4 and 1 and three should be close 1280: in value.*/ 1281: length2to4[i] = findDist(corner2[i].x, corner2[i].y, corner4[i].x, 1282: corner4[i].y); 1283: length1to3[i] = findDist(corner1[i].x, corner1[i].y, corner3[i].x, 1284: corner3[i].y); 1285: 1286: /*the other side pair*/ 1287: length1to4[i] = findDist(corner1[i].x, corner1[i].y, corner4[i].x, 1288: corner4[i].y); 1289: 1290: length2to3[i] = findDist(corner2[i].x, corner2[i].y, corner3[i].x, 1291: corner3[i].y); 1292: 1293: 1294: denom_check = fabs((length2to4[i] + length1to3[i]) / 2); 1295: if(denom_check == 0) 1296: { 1297: /*the only way to possibly pull this off is if one point is 1298: is considered more than one corner. This is most likely 1299: to happen where the chain was only two or three nodes long.*/ 1300: /*Just set the error to the maximum value obtained from float.h, 1301: since such a small chain is not what we are looking for.*/ 1302: 1303: error24to13[i] = FLT_MAX; 1304: } 1305: else 1306: error24to13[i] = fabs(length2to4[i] - length1to3[i]) / denom_check; 1307: /*fabs((length2to4[i] + length1to3[i]) / 2);*/ 1308: 1309: denom_check = fabs((length1to4[i] + length2to3[i]) / 2); 1310: if(denom_check == 0) 1311: { 1312: /*the only way to possibly pull this off is if one point is 1313: is considered more than one corner. This is most likely 1314: to happen where the chain was only two or three nodes long.*/ 1315: /*Just set the error to the maximum value obtained from float.h, 1316: since such a small chain is not what we are looking for.*/ 1317: 1318: error14to23[i] = FLT_MAX; 1319: } 1320: else 1321: error14to23[i] = fabs(length1to4[i] - length2to3[i]) / denom_check; 1322: /*fabs((length1to4[i] + length2to3[i]) / 2);*/ 1323: 1324: /*determine if the current object is considered the most likely to 1325: be the ttt board so far.*/ 1326: if(ml_error > ((error24to13[i] + error14to23[i]) / 2)) 1327: { 1328: most_likely = i; 1329: ml_error = ((error24to13[i] + error14to23[i]) / 2); 1330: } 1331: } 1332: 1333: printf("Object %d is most likely the board with %f\%% error.\n", 1334: most_likely, ml_error * 100); 1335: 1336: return most_likely; /*return the object number that is the board*/ 1337: } 1338: 1339: /*takes the object number that is determined to be the board*/ 1340: void detect_pieces(int object) 1341: { 1342: /*These hold intermidate values used to calculate where to look for 1343: each piece.*/ 1344: /*Note: These names won't necessarily correspond to the 1345: edges in the image. For head on shots, when they were calulated they 1346: seemed to go in this position during initial coding.*/ 1347: coord hor_top1, hor_top2, hor_bot1, hor_bot2; 1348: coord ver_rgt1, ver_rgt2, ver_lft1, ver_lft2; 1349: 1350: /*These store the calculated centers for where the pieces would be. 1351: Uses the global corners[] to calculate along with the coords 1352: declared above.*/ 1353: coord up_left, up_mid, up_right; 1354: coord mid_left, mid_mid, mid_right; 1355: coord low_left, low_mid, low_right; 1356: 1357: /*If there wasn't a board found in view, give up finding pieces.*/ 1358: if(object < 0) 1359: return; 1360: 1361: /*this code calculates where the dividers should be*/ 1362: hor_top1 = find_dividers(corner2[draw_abstract_board], 1363: corner3[draw_abstract_board], ONE_THIRD); 1364: hor_top2 = find_dividers(corner4[draw_abstract_board], 1365: corner1[draw_abstract_board], ONE_THIRD); 1366: 1367: hor_bot1 = find_dividers(corner3[draw_abstract_board], 1368: corner2[draw_abstract_board], ONE_THIRD); 1369: hor_bot2 = find_dividers(corner1[draw_abstract_board], 1370: corner4[draw_abstract_board], ONE_THIRD); 1371: 1372: ver_rgt1 = find_dividers(corner4[draw_abstract_board], 1373: corner2[draw_abstract_board], ONE_THIRD); 1374: ver_rgt2 = find_dividers(corner1[draw_abstract_board], 1375: corner3[draw_abstract_board], ONE_THIRD); 1376: 1377: ver_lft1 = find_dividers(corner2[draw_abstract_board], 1378: corner4[draw_abstract_board], ONE_THIRD); 1379: ver_lft2 = find_dividers(corner3[draw_abstract_board], 1380: corner1[draw_abstract_board], ONE_THIRD); 1381: /* 1382: setCPixel(hor_top1.x, hor_top1.y, dk_yellow); 1383: setCPixel(hor_top2.x, hor_top2.y, dk_magenta); 1384: setCPixel(hor_bot1.x, hor_bot1.y, dk_cyan); 1385: setCPixel(hor_bot2.x, hor_bot2.y, dk_red); 1386: setCPixel(ver_rgt1.x, ver_rgt1.y, dk_green); 1387: setCPixel(ver_rgt2.x, ver_rgt2.y, dk_blue); 1388: setCPixel(ver_lft1.x, ver_lft1.y, white); 1389: setCPixel(ver_lft2.x, ver_lft2.y, black); 1390: */ 1391: 1392: /*top row*/ 1393: up_left = find_dividers(corner1[draw_abstract_board], 1394: corner2[draw_abstract_board], ONE_SIXTH); 1395: up_mid = find_dividers(corner1[draw_abstract_board], 1396: hor_bot1, ONE_HALF); 1397: up_right = find_dividers(corner3[draw_abstract_board], 1398: corner4[draw_abstract_board], ONE_SIXTH); 1399: /*middle row*/ 1400: mid_left = find_dividers(corner1[draw_abstract_board], 1401: ver_rgt1, ONE_HALF); 1402: mid_mid = find_dividers(corner1[draw_abstract_board], 1403: corner2[draw_abstract_board], ONE_HALF); 1404: mid_right = find_dividers(corner2[draw_abstract_board], 1405: ver_lft2, ONE_HALF); 1406: /*bottom row*/ 1407: low_left = find_dividers(corner4[draw_abstract_board], 1408: corner3[draw_abstract_board], ONE_SIXTH); 1409: low_mid = find_dividers(corner2[draw_abstract_board], 1410: hor_top2, ONE_HALF); 1411: low_right = find_dividers(corner2[draw_abstract_board], 1412: corner1[draw_abstract_board], ONE_SIXTH); 1413: 1414: setCPixel(corner3[draw_abstract_board].x, corner3[draw_abstract_board].y, 1415: red); 1416: /*tow row*/ 1417: setCPixel(up_left.x, up_left.y, lt_green); 1418: setCPixel(up_mid.x, up_mid.y, lt_red); 1419: setCPixel(up_right.x, up_right.y, lt_blue); 1420: /*midle row*/ 1421: setCPixel(mid_left.x, mid_left.y, lt_green); 1422: setCPixel(mid_mid.x, mid_mid.y, lt_red); 1423: setCPixel(mid_right.x, mid_right.y, lt_blue); 1424: /*bottom row*/ 1425: setCPixel(low_left.x, low_left.y, lt_green); 1426: setCPixel(low_mid.x, low_mid.y, lt_red); 1427: setCPixel(low_right.x, low_right.y, lt_blue); 1428: } 1429: 1430: 1431: /* ================================================================= 1432: * Callback functions. 1433: * 1434: * color = displayed graphics in window 1435: * menu = menu event handling 1436: * keyboard = deyboard event handling 1437: * ----------------------------------------------------------------- */ 1438: void color(void) 1439: { 1440: /*glClear (GL_COLOR_BUFFER_BIT);*/ 1441: 1442: /* printf("\nDrawing Original image...\n");*/ 1443: showColor(img_cur); 1444: 1445: /*glFlush();*/ 1446: } 1447: 1448: void buffer() 1449: { 1450: /*if the drawing state of all objects is enabled, recalculate*/ 1451: if(draw_abstract_lines >= 0) 1452: draw_abstract_lines = detect_corners(img_cur); 1453: 1454: /*if the drawing state of the object most likely to be the board is 1455: found, recalulate*/ 1456: if(draw_abstract_board >= 0) 1457: draw_abstract_board = detect_game(detect_corners(img_cur)); 1458: 1459: glutPostRedisplay(); 1460: } 1461: 1462: #define RESTART 0 1463: #define CAMERA_CORRECTION 1 1464: #define X2_C_CORR 2 1465: #define NEW_CORR 3 1466: #define COLOR_TO_GRAY 4 1467: #define MORAVEC 5 1468: #define CORNERS 6 1469: #define BUFFERS 7 1470: #define GAME 8 1471: #define PIECES 9 1472: 1473: /*this is not a callback function, but is used inside menu() for setting 1474: new states of execution*/ 1475: void reset_state(PGMImage* new_current) 1476: { 1477: img_cur = new_current; 1478: draw_abstract_lines = -1; 1479: draw_abstract_board = -1; 1480: } 1481: 1482: void menu(int selection) 1483: { 1484: if(selection == RESTART) 1485: { 1486: reset_state(img0); 1487: } 1488: if(selection == CAMERA_CORRECTION) 1489: { 1490: camera_correction(img1, img0); 1491: reset_state(img1); 1492: } 1493: if(selection == X2_C_CORR) 1494: { 1495: camera_correction(img1, img0); 1496: camera_correction(img2, img1); 1497: reset_state(img2); 1498: } 1499: if(selection == NEW_CORR) 1500: { 1501: new_corr(img1, img0); 1502: reset_state(img1); 1503: } 1504: if(selection == COLOR_TO_GRAY) 1505: { 1506: color_to_gray(img1, img0); 1507: reset_state(img1); 1508: } 1509: if(selection == MORAVEC) 1510: { 1511: moravec(img1, img0); 1512: reset_state(img1); 1513: } 1514: if(selection == CORNERS) 1515: { 1516: draw_abstract_lines = detect_corners(img_cur); 1517: } 1518: if(selection == BUFFERS) 1519: { 1520: 1521: if(is_buffered == FALSE) 1522: { 1523: glutIdleFunc(buffer); 1524: is_buffered = TRUE; 1525: } 1526: else 1527: { 1528: glutIdleFunc(NULL); 1529: is_buffered = FALSE; 1530: } 1531: } 1532: if(selection == GAME) 1533: { 1534: draw_abstract_board = detect_game(detect_corners(img_cur)); 1535: } 1536: if(selection == PIECES) 1537: { 1538: draw_abstract_board = detect_game(detect_corners(img_cur)); 1539: detect_pieces(draw_abstract_board); 1540: return; 1541: } 1542: 1543: glutPostRedisplay(); /*redraw the image*/ 1544: } 1545: 1546: void keyboard(unsigned char key, int x, int y) 1547: { 1548: switch (key) 1549: { 1550: case 27: 1551: exit(0); 1552: break; 1553: } 1554: } 1555: 1556: void mouse(int button, int state, int x, int y) 1557: { 1558: char temp[50]; 1559: 1560: if(button == GLUT_LEFT_BUTTON && state == GLUT_DOWN) 1561: { 1562: sprintf(temp, "(x, y): (%d, %d) red: %d green: %d blue: %d\n", 1563: x, VSIZE - y, (*img_cur).data[VSIZE - y][x].red, 1564: (*img_cur).data[VSIZE - y][x].green, 1565: (*img_cur).data[VSIZE - y][x].blue); 1566: setCRect(0, 0, 200, 12, black); 1567: glColor3f(1.0, 0.0, 0.0); 1568: drawString(0, 0, GLUT_BITMAP_TIMES_ROMAN_10, temp); 1569: glFlush(); 1570: } 1571: } 1572: 1573: 1574: /* ================================================================= 1575: * init() & parse_command_line() 1576: * initalize none-OpenGL related things. 1577: * ----------------------------------------------------------------- */ 1578: 1579: /*set global flag variables from things specified at commandline.*/ 1580: /*return the filename specified, if none specified exit().*/ 1581: char* parse_command_line(int argc, char** argv) 1582: { 1583: /*parse the command line*/ 1584: if(argc == 1) 1585: { 1586: printf("To few parameters.\n"); 1587: printf("Usage: research <file.pgm>\n"); 1588: exit(1); 1589: } 1590: else if(argc == 2) 1591: { 1592: return argv[1]; 1593: } 1594: else 1595: { 1596: printf("To many parameters.\n"); 1597: printf("Usage: research <file.pgm>\n"); 1598: exit(1); 1599: } 1600: } 1601: 1602: char* init (int argc, char** argv) 1603: { 1604: char* PGMfileName;/*pointer to the string containing the filename*/ 1605: 1606: /*parse the command line*/ 1607: PGMfileName = parse_command_line(argc, argv); 1608: 1609: /* 1610: * Read in image file. - note: sets our global values, too. 1611: * ----------------------------------------------------------------- */ 1612: 1613: img0 = (PGMImage*) malloc(sizeof(PGMImage)); 1614: getPGMfile(PGMfileName, img0); 1615: HSIZE = (*img0).width; 1616: VSIZE = (*img0).height; 1617: MVAL = (*img0).maxVal; 1618: 1619: img_cur = img0; /*VERY IMPORTANT to set this*/ 1620: 1621: /*allocate memory for second image*/ 1622: img1 = (PGMImage*) malloc(sizeof(PGMImage)); 1623: memcpy(img1, img0, sizeof(PGMImage)); 1624: /*(*img1).width = HSIZE; 1625: (*img1).height = VSIZE; 1626: (*img1).maxVal = 255;*/ 1627: 1628: img2 = (PGMImage*) malloc(sizeof(PGMImage)); 1629: memcpy(img2, img0, sizeof(PGMImage)); 1630: 1631: memset(corner1, 0, sizeof(coord) * MAX_CHAINS); 1632: memset(corner2, 0, sizeof(coord) * MAX_CHAINS); 1633: memset(corner3, 0, sizeof(coord) * MAX_CHAINS); 1634: memset(corner4, 0, sizeof(coord) * MAX_CHAINS); 1635: 1636: return PGMfileName; 1637: } 1638: 1639: 1640: int main(int argc, char** argv) 1641: { 1642: char* PGMfileName; 1643: int WindowID; /*unique window id, there is only one used*/ 1644: int i; /*looping variable*/ 1645: 1646: /* 1647: * Call our init function to define non-OpenGL related things. 1648: * Have init return a pointer to the filename, used to display the 1649: * filename in the titlebar of the window. 1650: * ----------------------------------------------------------------- */ 1651: PGMfileName = init (argc, argv); 1652: 1653: /* 1654: * Initialize the glut package. 1655: * ----------------------------------------------------------------- */ 1656: glutInit(&argc, argv); 1657: glutInitDisplayMode (GLUT_SINGLE | GLUT_RGB); 1658: 1659: /* 1660: * Define a new window (its size, position and title). 1661: * ----------------------------------------------------------------- */ 1662: glutInitWindowSize (HSIZE, VSIZE); /*size*/ 1663: glutInitWindowPosition (10, 10); /*position*/ 1664: WindowID = glutCreateWindow (PGMfileName); /*title*/ 1665: glutSetWindow(WindowID); 1666: glutDisplayFunc(color); 1667: 1668: /* 1669: * select clearing color - white 1670: */ 1671: glClearColor (1.0, 1.0, 1.0, 0.0); 1672: 1673: /* 1674: * initialize viewport values 1675: */ 1676: glMatrixMode(GL_PROJECTION); 1677: glLoadIdentity(); 1678: glOrtho(-1.0, 1.0, -1.0, 1.0, -1.0, 1.0); 1679: 1680: /*add menus*/ 1681: glutCreateMenu(menu); 1682: glutAddMenuEntry("Restart", RESTART); 1683: /* glutAddMenuEntry("Camera Correction", CAMERA_CORRECTION); 1684: glutAddMenuEntry("2x C. Corr.", X2_C_CORR);*/ 1685: glutAddMenuEntry("new corr", NEW_CORR); 1686: glutAddMenuEntry("color to gray", COLOR_TO_GRAY); 1687: glutAddMenuEntry("moravec", MORAVEC); 1688: glutAddMenuEntry("corners", CORNERS); 1689: glutAddMenuEntry("toggle buffering", BUFFERS); 1690: glutAddMenuEntry("detect game", GAME); 1691: glutAddMenuEntry("detect pieces", PIECES); 1692: glutAttachMenu(GLUT_RIGHT_BUTTON); 1693: 1694: glutMouseFunc(mouse); 1695: glutKeyboardFunc(keyboard); 1696: glutMainLoop(); 1697: 1698: /* 1699: * When we reach here, we've left the event loop and are ready to 1700: * exit. 1701: * ----------------------------------------------------------------- */ 1702: return 0; 1703: } 1704: 1705: 1706: 1707: 1708: 1709: 1710: 1711: 1712: 1713: 1714: 1715: 1716: