1: #include <stdlib.h> 2: #include <stdio.h> 3: #include <string.h> 4: #include <math.h> 5: #include <GL/glut.h> 6: 7: #include "pgm.h" 8: #include "chaincode.h" 9: 10: #define Round(v) ((int)(v+0.5)) 11: 12: /*minimal size to consider an "object" as length of chaincode*/ 13: #define DECENT_SIZED_OBJECT 100 14: 15: #define ONE_THIRD (1.0 / 3.0) 16: #define ONE_HALF (1.0 / 2.0) 17: #define ONE_SIXTH (1.0 / 6.0) 18: #define ONE_TWELTH (1.0 / 12.0) 19: 20: typedef struct 21: { 22: coord corner1; 23: coord corner2; 24: coord corner3; 25: coord corner4; 26: }object; 27: 28: typedef struct 29: { 30: /*The corners of the board. corner1 and corner2 are always opposite 31: (diagnol), just like corner3 and corner4 too.*/ 32: object corners; 33: 34: /*The sides. The numbers represent the to corners that the point is 35: between. The first number is the corner that it is closest to.*/ 36: coord side14, side13, side23, side24; 37: coord side31, side32, side41, side42; 38: 39: /*The points surrounding the middle square. The number indicates the 40: closest corner.*/ 41: coord middle1, middle2, middle3, middle4; 42: }ttt; 43: 44: /*Evil globals, but not do-able otherwise.*/ 45: static PGMImage *img_cur; /*current*/ 46: static PGMImage *img_original; /*original*/ 47: static PGMImage *img_pers_corr; /*perspective correction*/ 48: static PGMImage *img_grayscale; 49: static PGMImage *img_moravec; /*moravec*/ 50: static int HSIZE; 51: static int VSIZE; 52: static int MVAL; 53: 54: static ttt *ttt_board; /*pointer to a ttt structer containing the board info*/ 55: 56: const RGB_INT white = {255, 255, 255}; 57: const RGB_INT yellow = {255, 255, 0}; 58: const RGB_INT magenta = {255, 0, 255}; 59: const RGB_INT cyan = {0, 255, 255}; 60: const RGB_INT red = {255, 0, 0}; 61: const RGB_INT green = {0, 255, 0}; 62: const RGB_INT blue = {0, 0, 255}; 63: const RGB_INT black = {0, 0, 0}; 64: 65: const RGB_INT gray = {128, 128, 128}; 66: const RGB_INT lt_yellow = {255, 255, 128}; 67: const RGB_INT lt_magenta = {255, 128, 255}; 68: const RGB_INT lt_cyan = {128, 255, 255}; 69: 70: const RGB_INT lt_red = {255, 128, 128}; 71: const RGB_INT lt_green = {128, 255, 128}; 72: const RGB_INT lt_blue = {128, 128, 255}; 73: 74: const RGB_INT dk_yellow = {128, 128, 0}; 75: const RGB_INT dk_magenta = {128, 0, 128}; 76: const RGB_INT dk_cyan = { 0, 128, 128}; 77: 78: const RGB_INT dk_red = {128, 0, 0}; 79: const RGB_INT dk_green = { 0, 128, 0}; 80: const RGB_INT dk_blue = { 0, 0, 128}; 81: 82: /*pointer to an array of pointers which point to the first nodes in each of 83: the chain codes.*/ 84: list_info* chain_codes; 85: 86: /*hold the points farthest away from each other for each object*/ 87: /*coord corner1[MAX_CHAINS], corner2[MAX_CHAINS]; 88: coord corner3[MAX_CHAINS], corner4[MAX_CHAINS];*/ 89: /*object all_objects[MAX_CHAINS];*/ 90: static object *all_objects; 91: 92: /*use double buffered output and keep running enable idle callback to call 93: detect_corners() over and over simulating realtime response. 94: Is set to TRUE or FALSE.*/ 95: int is_buffered; 96: 97: /*used to determine if abstract lines should be drawn to the screen. 98: Set to <0 if no abstract lines are to be drawn. Otherwise 99: is set to the number of objects found (AKA number of chaincodes).*/ 100: int draw_abstract_lines = -1; 101: 102: /*used to draw the single object most likely to be considered the 103: tic-tac-toe board. Should be <0 if this should not be drawn. 104: Should be equal to the chain-code number (from 0 to MAX_CHAINS - 1) 105: if it is to be drawn.*/ 106: int draw_abstract_board = -1; 107: 108: 109: /**************Drawing funcitions************************************/ 110: /********************************************************************/ 111: 112: void setCPixel(int ix, int iy, RGB_INT color) 113: /*Same as setIPixel except that the last parameter is an RGB color*/ 114: { 115: float x = (ix*2.0)/HSIZE - 1.0; 116: float y = (iy*2.0)/VSIZE - 1.0; 117: 118: float red = (float)color.red/(float)MVAL; 119: float green = (float)color.green/(float)MVAL; 120: float blue = (float)color.blue/(float)MVAL; 121: 122: glColor3f(red, green, blue); 123: 124: glBegin(GL_POINTS); 125: glVertex2f (x, y); 126: glEnd(); 127: } 128: 129: void setCLines(int ix1, int iy1, int ix2, int iy2, RGB_INT color) 130: /*Similar as setIPixel except that this one draws a line between the first set 131: of points given and the second set in the RGB color specified*/ 132: { 133: float x1 = (ix1*2.0)/HSIZE - 1.0; 134: float y1 = (iy1*2.0)/VSIZE - 1.0; 135: float x2 = (ix2*2.0)/HSIZE - 1.0; 136: float y2 = (iy2*2.0)/VSIZE - 1.0; 137: 138: float red = (float)color.red/(float)MVAL; 139: float green = (float)color.green/(float)MVAL; 140: float blue = (float)color.blue/(float)MVAL; 141: 142: glColor3f(red, green, blue); 143: 144: glBegin(GL_LINES); 145: glVertex2f (x1, y1); 146: glVertex2f (x2, y2); 147: glEnd(); 148: } 149: 150: void setCRect(int ix1, int iy1, int ix2, int iy2, RGB_INT color) 151: /*Similar as setIPixel except that this one draws a line between the first set 152: of points given and the second set in the RGB color specified*/ 153: { 154: float x1 = (ix1*2.0)/HSIZE - 1.0; 155: float y1 = (iy1*2.0)/VSIZE - 1.0; 156: float x2 = (ix2*2.0)/HSIZE - 1.0; 157: float y2 = (iy2*2.0)/VSIZE - 1.0; 158: 159: float red = (float)color.red/(float)MVAL; 160: float green = (float)color.green/(float)MVAL; 161: float blue = (float)color.blue/(float)MVAL; 162: 163: glColor3f(red, green, blue); 164: 165: glBegin(GL_POLYGON); 166: glVertex2f (x1, y1); 167: glVertex2f (x1, y2); 168: glVertex2f (x2, y2); 169: glVertex2f (x2, y1); 170: glEnd(); 171: } 172: 173: /* ================================================================= 174: * drawString - outputs a string of characters to the graphics port 175: * 176: * x, y: defines the starting location to draw the text 177: * note: this point is the lower left anchor of 178: * the first character - a character's decending 179: * portion would be drawn below this point. 180: * theFont: points to the glut font to be used 181: * theString: holds the string to be output -- up to 255 ch 182: * ----------------------------------------------------------------- */ 183: void drawString(int ix, int iy, void *theFont, char theString[256]) 184: { 185: float x = (ix*2.0)/HSIZE - 1.0; 186: float y = (iy*2.0)/VSIZE - 1.0; 187: int i; 188: glRasterPos2f(x, y); 189: for (i = 0; theString[i] != '\0'; i++) /* draw the chars one at a time */ 190: glutBitmapCharacter(theFont, theString[i]); 191: } 192: 193: void showColor (PGMImage *img) 194: { 195: int i, j; /*loop counting: i = y, j = x*/ 196: 197: GLubyte checkImage[(*img).height][(*img).width][3]; 198: 199: for(i = 0; i < (*img).height; i++) 200: { 201: for(j = 0; j < (*img).width; j++) 202: { 203: checkImage[i][j][0] = (GLubyte) (*img).data[i][j].red; 204: checkImage[i][j][1] = (GLubyte) (*img).data[i][j].green; 205: checkImage[i][j][2] = (GLubyte) (*img).data[i][j].blue; 206: } 207: } 208: /*draw the current image*/ 209: glPixelStorei(GL_UNPACK_ALIGNMENT, 1); 210: glRasterPos2f(-1, -1); 211: glDrawPixels((*img).width, (*img).height, GL_RGB, 212: GL_UNSIGNED_BYTE, checkImage); 213: 214: glFlush(); 215: } 216: 217: /*display the global abstract data*/ 218: void showAbstract(object* object_list, ttt *ttt_data) 219: { 220: int i; 221: 222: /* coord hor_top1, hor_top2; 223: coord hor_bot1, hor_bot2; 224: coord ver_rgt1, ver_rgt2; 225: coord ver_lft1, ver_lft2; 226: */ 227: list_info temp; 228: 229: glPointSize(2); /*make points more visible, if desired*/ 230: /*glLineWidth(4);*/ 231: 232: /*draw the chaincodes*/ 233: /*chain_codes is a global pointer to an array of list_info types*/ 234: for(i = 0; (i < MAX_CHAINS) && chain_codes && chain_codes[i].cur; i++) 235: { 236: memcpy(&temp, &chain_codes[i], sizeof(list_info)); 237: while(RetrieveNextNode(&temp).cur) 238: { 239: setCPixel(RetrieveInfo(&temp).location.x, 240: RetrieveInfo(&temp).location.y, gray); 241: Advance(&temp); 242: } 243: } 244: 245: /*first check for non-null pointer, next check for dereferenced pointers 246: in the array of head pointers and lastly make sure things stay in 247: bound of the max incase all MAX_CHAINS number of chains are used.*/ 248: /*draw_abstract_lines is the global that holds the number of "objects" 249: to draw abstract information for*/ 250: for(i = 0; i < draw_abstract_lines && i < MAX_CHAINS; i++) 251: { 252: setCLines(object_list[i].corner1.x, object_list[i].corner1.y, 253: object_list[i].corner3.x, object_list[i].corner3.y,yellow); 254: setCLines(object_list[i].corner4.x, object_list[i].corner4.y, 255: object_list[i].corner2.x, object_list[i].corner2.y,yellow); 256: setCLines(object_list[i].corner3.x, object_list[i].corner3.y, 257: object_list[i].corner2.x, object_list[i].corner2.y,yellow); 258: setCLines(object_list[i].corner4.x, object_list[i].corner4.y, 259: object_list[i].corner1.x, object_list[i].corner1.y,yellow); 260: 261: setCPixel(object_list[i].corner1.x, object_list[i].corner1.y,dk_red); 262: setCPixel(object_list[i].corner2.x, object_list[i].corner2.y,dk_blue); 263: setCPixel(object_list[i].corner3.x, object_list[i].corner3.y,white); 264: setCPixel(object_list[i].corner4.x, object_list[i].corner4.y,dk_green); 265: } 266: 267: /*if there is board to draw and just make sure there isn't a NULL pointer*/ 268: if((draw_abstract_board > -1) && ttt_data) 269: { 270: /*This code should draw the bounding box*/ 271: /*setCLines(ttt_data->corners.corner1.x, 272: ttt_data->corners.corner1.y, 273: ttt_data->corners.corner3.x, 274: ttt_data->corners.corner3.y,blue); 275: setCLines(ttt_data->corners.corner4.x, 276: ttt_data->corners.corner4.y, 277: ttt_data->corners.corner2.x, 278: ttt_data->corners.corner2.y,blue); 279: setCLines(ttt_data->corners.corner3.x, 280: ttt_data->corners.corner3.y, 281: ttt_data->corners.corner2.x, 282: ttt_data->corners.corner2.y,blue); 283: setCLines(ttt_data->corners.corner4.x, 284: ttt_data->corners.corner4.y, 285: ttt_data->corners.corner1.x, 286: ttt_data->corners.corner1.y,blue); 287: setCPixel(ttt_data->corners.corner1.x, 288: ttt_data->corners.corner1.y, red); 289: setCPixel(ttt_data->corners.corner2.x, 290: ttt_data->corners.corner2.y, red); 291: setCPixel(ttt_data->corners.corner3.x, 292: ttt_data->corners.corner3.y, red); 293: setCPixel(ttt_data->corners.corner4.x, 294: ttt_data->corners.corner4.y, red); 295: */ 296: 297: setCLines(ttt_data->side13.x, 298: ttt_data->side13.y, 299: ttt_data->side42.x, 300: ttt_data->side42.y,blue); 301: setCLines(ttt_data->side31.x, 302: ttt_data->side31.y, 303: ttt_data->side24.x, 304: ttt_data->side24.y,blue); 305: setCLines(ttt_data->side23.x, 306: ttt_data->side23.y, 307: ttt_data->side41.x, 308: ttt_data->side41.y,blue); 309: setCLines(ttt_data->side32.x, 310: ttt_data->side32.y, 311: ttt_data->side14.x, 312: ttt_data->side14.y,blue); 313: 314: setCPixel(ttt_data->middle1.x, ttt_data->middle1.y, red); 315: setCPixel(ttt_data->middle2.x, ttt_data->middle2.y, red); 316: setCPixel(ttt_data->middle3.x, ttt_data->middle3.y, red); 317: setCPixel(ttt_data->middle4.x, ttt_data->middle4.y, red); 318: } 319: glFlush(); 320: } 321: 322: /**********************Support functions*******************************/ 323: /***********************************************************************/ 324: 325: void pxlcpy(PGMImage *dest, int dest_row, int dest_col, 326: PGMImage *src, int src_row, int src_col) 327: { 328: /*make sure values are within bounds*/ 329: if(dest_col > 0 && dest_col < (*dest).width 330: && dest_row > 0 && dest_row < (*dest).height 331: && src_col > 0 && src_col < (*src).width 332: && src_row > 0 && src_row < (*src).height) 333: { 334: (*dest).data[dest_row][dest_col].red = 335: (*src).data[src_row][src_col].red; 336: 337: (*dest).data[dest_row][dest_col].green = 338: (*src).data[src_row][src_col].green; 339: 340: (*dest).data[dest_row][dest_col].blue = 341: (*src).data[src_row][src_col].blue; 342: } 343: } 344: 345: int rgb_avg(RGB_INT cur_pxl) 346: { 347: /*convert each RGB to the average of the original*/ 348: return ((cur_pxl.red + cur_pxl.green + cur_pxl.blue) / 3); 349: } 350: 351: /*Returns average (with RGB avg) pixel value for the image passed in.*/ 352: int img_pxl_avg(PGMImage* img) 353: { 354: int i, j; /*loop counting*/ 355: int sum = 0; 356: int temp; 357: 358: for(i = 0; i < (*img).height; i++)/*collumn*/ 359: for(j = 0; j < (*img).width; j++)/*row*/ 360: sum += rgb_avg((*img).data[i][j]); 361: 362: printf("sum: %d height: %d width: %d\n", sum, (*img).height, (*img).width); 363: temp = (sum / ((*img).height * (*img).width)); 364: printf("temp: %d\n", temp); 365: return temp; 366: } 367: 368: /*1st: pixel one of type RGB_INT 369: 2nd: pixel one of type RGB_INT 370: 3rd: differnce allowed to be considered "equal" or close enough*/ 371: int pxlcmp (RGB_INT pxl1, RGB_INT pxl2, int range) 372: { 373: return ((abs((rgb_avg(pxl1) - rgb_avg(pxl2)))) < range); 374: } 375: 376: /*return >0 if number of pixels is greater than img. pxl. avg., return <0 if 377: number of pixesl is less than img. pxl. avg. and return zero of equal*/ 378: int background(int treash_value, PGMImage* img) 379: { 380: int i, j; /*loop counting*/ 381: int pxl_less = 0, pxl_more = 0; 382: 383: for(i = 0; i < (*img).height; i++)/*collumn*/ 384: for(j = 0; j < (*img).width; j++)/*row*/ 385: { 386: if(rgb_avg((*img).data[i][j]) < treash_value) 387: pxl_less++; 388: 389: if(rgb_avg((*img).data[i][j]) > treash_value) 390: pxl_more++; 391: } 392: 393: if(pxl_less > pxl_more) 394: return -1; 395: else if(pxl_less < pxl_more) 396: return 1; 397: else 398: return 0; 399: } 400: 401: /*Used by showColor() and detect_pieces()*/ 402: coord find_dividers(coord point1, coord point2, float t) 403: { 404: coord temp; 405: 406: temp.x = (int)(((1.0 - t) * point1.x) + (t * point2.x)); 407: temp.y = (int)(((1.0 - t) * point1.y) + (t * point2.y)); 408: 409: return temp; 410: } 411: 412: /******Perspective correction******************************************* 413: ***********************************************************************/ 414: void pers_corr(PGMImage* new_img, PGMImage* org_img) 415: { 416: /*i and j are the left half, k and l are the right half*/ 417: float i, k; /*loop counting*/ 418: int j, l, row; /*loop counting*/ 419: int old_i, old_k; 420: 421: float ins_s = 2.0; /*insert constant starting value*/ 422: float ins_k = ins_s; /*insert constant*/ 423: 424: /*The halfway marks in the width.*/ 425: int mid_width_left = ((*new_img).width / 2) - 1; 426: int mid_width_right = ((*new_img).width / 2); 427: 428: /*just to be thourough clear the memory and reset maxes*/ 429: memset(new_img, 0, sizeof(PGMImage)); 430: (*new_img).height = (*org_img).height; 431: (*new_img).width = (*org_img).width; 432: (*new_img).maxVal = (*org_img).maxVal; 433: 434: /****x direction correction******/ 435: 436: /*Loop through each row from top to bottom...*/ 437: for(row = ((*new_img).height - 1); row >= 0; row--) 438: { 439: /*...reset moire interference removal counter...*/ 440: old_i = ((*new_img).width / 2) - 1; 441: old_k = ((*new_img).width / 2); 442: 443: /*...so each half is ajusted to remove perspective effect...*/ 444: for(i = j = mid_width_left, k = l = mid_width_right 445: ; i >= 0, j >= 0, k < (*new_img).width, l < (*new_img).width 446: ; i -= ins_k, j--, k += ins_k, l++) 447: { 448: for(;old_i >= (int)i; old_i--) /*...in the left half...*/ 449: pxlcpy(new_img, row, old_i, org_img, row, j); 450: for(;old_k <= (int)k; old_k++) /*...in the right half.*/ 451: pxlcpy(new_img, row, old_k, org_img, row, l); 452: } 453: /*Move the new image x_coord pixel counter to next new image pixel*/ 454: ins_k -= ((ins_s - 1.0) / (*new_img).height); 455: } 456: } 457: 458: /*****convert color to grayscale**************************************** 459: ***********************************************************************/ 460: void color_to_gray(PGMImage* new_img, PGMImage* org_img) 461: { 462: int row, col; /*loop counting*/ 463: RGB_INT cur_pxl; /*current pixel*/ 464: 465: (*new_img).height = (*org_img).height; 466: (*new_img).width = (*org_img).width; 467: (*new_img).maxVal = (*org_img).maxVal; 468: 469: /*Starting with the top row...*/ 470: for(row = (*new_img).height - 1; row >= 0; row--) 471: for(col = 0; col < (*new_img).width - 1; col++) 472: { 473: cur_pxl = (*org_img).data[row][col]; /*more readable*/ 474: 475: /*convert each RGB to the average of the original*/ 476: (*new_img).data[row][col].red = rgb_avg(cur_pxl); 477: (*new_img).data[row][col].green = rgb_avg(cur_pxl); 478: (*new_img).data[row][col].blue = rgb_avg(cur_pxl); 479: } 480: } 481: 482: /*******Edge highlighting********************************************** 483: **********************************************************************/ 484: void moravec(PGMImage* new_img, PGMImage* org_img) 485: { 486: int row, col; /*loop counting*/ 487: int i, j, k, l; /*Sanka, Hlavac & Boyle; p. 97 f. 4.73*/ 488: int running_sum; 489: float K = .5; /*.125 according to org. formula, but .5 is brighter*/ 490: int max_val = 0, row_max, col_max; /* max porportion value in image*/ 491: printf("width: %d\n", (*org_img).height); 492: memset(new_img, 0, sizeof(PGMImage)); 493: (*new_img).height = (*org_img).height; 494: (*new_img).width = (*org_img).width; 495: (*new_img).maxVal = (*org_img).maxVal; 496: 497: /*starting at the top row*/ 498: for(row = (*new_img).height - 1 - 1; row > 0; row--) 499: for(col = 1; col < (*new_img).width - 1; col++) /*left col start*/ 500: { 501: i = row; 502: j = col; 503: running_sum = 0; 504: 505: /*Sanka, Hlavac & Boyle; p. 97 f. 4.73*/ 506: for(k = i - 1; k <= i + 1; k++) /*row*/ 507: for(l = j - 1; l <= j + 1; l++) /*column*/ 508: running_sum += abs(rgb_avg((*org_img).data[k][l]) - 509: rgb_avg((*org_img).data[i][j])); 510: 511: /*assign the new pixel value*/ 512: /*since it all the data is initalized to 0, we only worry when it 513: shouldn't be*/ 514: if((int)(K * running_sum) >= 128) 515: { 516: (*new_img).data[row][col].red = 255; 517: (*new_img).data[row][col].green = 255; 518: (*new_img).data[row][col].blue = 255; 519: } 520: /*this is the original code...*/ 521: /*(*new_img).data[row][col].red = (int)(K * running_sum); 522: (*new_img).data[row][col].green = (int)(K * running_sum); 523: (*new_img).data[row][col].blue = (int)(K * running_sum);*/ 524: } 525: } 526: 527: /*******Corners***************************************************** 528: *******************************************************************/ 529: 530: /*takes two coordinates as x and y pairs and returns the distence betweem them 531: as a decimal*/ 532: float findDist(int x1, int y1, int x2, int y2) 533: { 534: return sqrt(((x1 - x2) * (x1 - x2)) + ((y1 - y2) * (y1 - y2))); 535: } 536: 537: /*1st param: The array of coords that the first point of the major axis is 538: returned in. 539: 2nd: The array of coords that the second point of the major axis is 540: returned in. 541: 3rd: The array of chain codes that are searched through to find the major 542: axes.*/ 543: /* Note: the ending condition is when a NULL chainCodes value is reached. 544: Thusly, it works like a string requiring it to have the last legal value 545: followed by a null value.*/ 546: void findFirstTwoCorners(object *objects_array, list_info* chainCodes) 547: { 548: int i; /*loop counting*/ 549: list_info temp, search; 550: double max_dist, test_dist; 551: 552: /*printf("\nFinding first 2 corners.\n");*/ 553: 554: /*as long as there are codes to check, keep checking. Note: the ending 555: condition is when a NULL chainCodes value is reached. Thusly, it works 556: like a string requiring it to have the last legal value followed by a 557: null value.*/ 558: for(i = 0; ((i < MAX_CHAINS) && chainCodes[i].cur); i++) 559: { 560: memcpy(&temp, &chainCodes[i], sizeof(list_info)); 561: 562: max_dist = 0.0; /*reset this for each iteration*/ 563: 564: /*printf("checking list: %d\n", i);*/ 565: 566: while(RetrieveNextNode(&temp).cur) /*while there are nodes to check*/ 567: { 568: /*set the faster moving search pointer to temp, 569: this increases the effiecency a lot compared to 570: setting it equal to the first node..*/ 571: memcpy(&search, &temp, sizeof(list_info)); 572: 573: while(RetrieveNextNode(&search).cur) 574: { 575: /*setCPixel(RetrieveInfo(&temp).location.x, 576: RetrieveInfo(&temp).location.y, green);*/ 577: 578: /*determine if found a new maximum distance between two locations*/ 579: if((test_dist = findDist(RetrieveInfo(&search).location.x, 580: RetrieveInfo(&search).location.y, 581: RetrieveInfo(&temp).location.x, 582: RetrieveInfo(&temp).location.y))>max_dist) 583: { 584: max_dist = test_dist; 585: objects_array[i].corner1.x = RetrieveInfo(&temp).location.x; 586: objects_array[i].corner1.y = RetrieveInfo(&temp).location.y; 587: objects_array[i].corner2.x = RetrieveInfo(&search).location.x; 588: objects_array[i].corner2.y = RetrieveInfo(&search).location.y; 589: } 590: Advance(&search); 591: } 592: Advance(&temp); 593: } 594: /*printf("point1: %d %d\n", max1[i].x, max1[i].y); 595: printf("point2: %d %d\n", max2[i].x, max2[i].y);*/ 596: } 597: } 598: 599: /*1st param: Array of coords for the first corner of each chain code. 600: 2nd param: Array of coords for the second corner of each chain code. 601: The first two parameters should equal the first two parameters "returned" 602: from the findFirstTwoCorners() function. 603: 3rd: Array of coords "returned" with the third corners. 604: 4th: Array of coords "returned" with the fourth corners. 605: 5th: Pointer pointing to the array of chaincode pointers, obtained from 606: showChain().*/ 607: void findSecondTwoCorners(object *objects_array, list_info* chain_code_array) 608: { 609: int i; /*loop counting*/ 610: list_info temp; 611: float temp_dist1, temp_dist2; /*distance between point and each corner*/ 612: coord canidate_coord1, temp_coord; 613: float canidate_dist1, max_dist; 614: int corner_count; 615: 616: /*printf("\nFinding last 2 corners.\n\n");*/ 617: 618: /*for each chain code find the corners*/ 619: for(i = 0; (i < MAX_CHAINS) && chain_code_array[i].cur; i++) 620: { 621: memcpy(&temp, &chain_code_array[i], sizeof(list_info)); 622: 623: /*reset these for the next chain code*/ 624: max_dist = 0.0; 625: corner_count = 1; 626: 627: /*while there are nodes in the chain code to check*/ 628: /*if there isn't a next node cur is NULL, which is checked*/ 629: while(RetrieveNextNode(&temp).cur) 630: { 631: /*setCPixel(RetrieveInfo(&temp).location.x, 632: RetrieveInfo(&temp).location.y, color1);*/ 633: 634: /*determine the first canidate coord for corner 3/4*/ 635: if(((RetrieveInfo(&temp).location.x == objects_array[i].corner1.x) 636: && (RetrieveInfo(&temp).location.y == objects_array[i].corner1.y)) 637: || ((RetrieveInfo(&temp).location.x == objects_array[i].corner2.x) 638: &&(RetrieveInfo(&temp).location.y == objects_array[i].corner2.y))) 639: { 640: /*if this corner found is the first of the two allready known 641: corners, then set the first canidate coord data and reset data 642: to find the next canidate corner point*/ 643: if(corner_count == 1) 644: { 645: canidate_coord1.x = temp_coord.x; 646: canidate_coord1.y = temp_coord.y; 647: canidate_dist1 = max_dist; 648: 649: corner_count = 2; /*set for next corner*/ 650: max_dist = 0.0; 651: } 652: else if(corner_count == 2) 653: { 654: /*the second canidate is always a corner*/ 655: all_objects[i].corner4.x = temp_coord.x; 656: all_objects[i].corner4.y = temp_coord.y; 657: 658: max_dist = 0.0; /*set for next corner canidate*/ 659: } 660: } 661: 662: /*calculate the distance between the current point being checked and 663: each corner point*/ 664: temp_dist1 = findDist(all_objects[i].corner1.x, 665: all_objects[i].corner1.y, 666: RetrieveInfo(&temp).location.x, 667: RetrieveInfo(&temp).location.y); 668: temp_dist2 = findDist(all_objects[i].corner2.x, 669: all_objects[i].corner2.y, 670: RetrieveInfo(&temp).location.x, 671: RetrieveInfo(&temp).location.y); 672: 673: /*if the current point is the furthest away sofar, store this point 674: untill it is overridden or becomes a canidate point*/ 675: if((temp_dist1 + temp_dist2) > max_dist) 676: { 677: temp_coord.x = RetrieveInfo(&temp).location.x; 678: temp_coord.y = RetrieveInfo(&temp).location.y; 679: 680: max_dist = (temp_dist1 + temp_dist2); 681: } 682: 683: Advance(&temp); 684: } 685: 686: /*from the three canidate coords find the two real corners.*/ 687: /*the second canidate will always be a corner, must test 1 vs 3, where 688: three is in the variables temp_coord and max_dist.*/ 689: if(canidate_dist1 > max_dist) /*first canidate*/ 690: { 691: all_objects[i].corner3.x = canidate_coord1.x; 692: all_objects[i].corner3.y = canidate_coord1.y; 693: } 694: else /*third canidate*/ 695: { 696: all_objects[i].corner3.x = temp_coord.x; 697: all_objects[i].corner3.y = temp_coord.y; 698: } 699: /*printf("corner3: (%d, %d) corner4: (%d, %d)\n", corner3[i].x, 700: corner3[i].y, corner4[i].x, corner4[i].y);*/ 701: } 702: } 703: 704: /*takes a pointer to an image, and a pointer pointing to an array of 705: chain codes pointers, here each chainCode pointer needs to be accessed 706: by calculating the memory address.*/ 707: void showBound(/*PGMImage *original,*/ list_info* chainCodes, object *all_objects) 708: { 709: int i; 710: 711: /*find the first two corners. they will be across a diagnal.*/ 712: findFirstTwoCorners(all_objects, chainCodes); 713: /*find the second two corners. they will be across a diagnal too.*/ 714: findSecondTwoCorners(all_objects, chainCodes); 715: /* 716: for(i = 0; chainCodes[i] && i < MAX_CHAINS; i++) 717: { 718: setCLines(corner1[i].x, corner1[i].y, corner3[i].x, corner3[i].y,yellow); 719: setCLines(corner4[i].x, corner4[i].y, corner2[i].x, corner2[i].y,yellow); 720: setCLines(corner3[i].x, corner3[i].y, corner2[i].x, corner2[i].y,yellow); 721: setCLines(corner4[i].x, corner4[i].y, corner1[i].x, corner1[i].y,yellow); 722: setCPixel(corner1[i].x, corner1[i].y, magenta); 723: setCPixel(corner2[i].x, corner2[i].y, magenta); 724: setCPixel(corner3[i].x, corner3[i].y, magenta); 725: setCPixel(corner4[i].x, corner4[i].y, magenta); 726: }*/ 727: } 728: 729: /*returns the number of objects found*/ 730: /*places the corner info in the global corners data*/ 731: int detect_corners(list_info* current_chaincodes, 732: object *objects_array) 733: { 734: int i; /*temporarily holds number of chaincodes*/ 735: 736: showBound(/*org_img,*/ current_chaincodes, objects_array); 737: 738: /*when this stops, i holds the number of chaincodes*/ 739: for(i = 0; (i < MAX_CHAINS) && chain_codes && chain_codes[i].cur; i++); 740: 741: return i; 742: } 743: 744: /*******Find the game board******************************************** 745: **********************************************************************/ 746: 747: float compare_length(float length1, float length2) 748: { 749: float denom_check = fabs((length1 + length2) / 2); 750: if(denom_check == 0) 751: { 752: /*the only way to possibly pull this off is if one point is 753: is considered more than one corner. This is most likely 754: to happen where the chain was only two or three nodes long.*/ 755: /*Just set the error to the maximum value obtained from float.h, 756: since such a small chain is not what we are looking for.*/ 757: 758: /*error24to13[i] =*/ return FLT_MAX; 759: } 760: else 761: /*error24to13[i] =*/return fabs(length1 - length2) / denom_check; 762: } 763: 764: /*determines which object is the game board and determine moves made in 765: the game.*/ 766: /*takes the number of object corners (equal to # of chaincodes) as param.*/ 767: int detect_game(list_info *current_chaincodes, object *object_array) 768: { 769: float length2to4[MAX_CHAINS], length1to3[MAX_CHAINS]; /*side pairs*/ 770: float length1to4[MAX_CHAINS], length2to3[MAX_CHAINS]; /*side pairs*/ 771: float length1to2[MAX_CHAINS], length3to4[MAX_CHAINS]; /*diagnaols*/ 772: 773: float error24to13[MAX_CHAINS], error14to23[MAX_CHAINS];/*opp. sides*/ 774: float error14to13[MAX_CHAINS], error23to24[MAX_CHAINS];/*share corner*/ 775: float error31to32[MAX_CHAINS], error41to42[MAX_CHAINS];/*share corner*/ 776: float error12to34[MAX_CHAINS];/*diagnaols*/ 777: 778: float error_avg;/*average of the errors stored in error##to## variables*/ 779: 780: int i, k; /*loop counting*/ 781: /*float denom_check; /*make sure denominators are not zero*/ 782: list* temp; 783: 784: /*the most likely object (0 to num_of_corners) that is to be considered 785: as the board. The float is the error associated with this object.*/ 786: int most_likely = -1; 787: float ml_error = FLT_MAX; /*just to make sure*/ 788: printf("here inside detect game\n"); 789: /*for each chaincode*/ 790: for(i = 0; (i < MAX_CHAINS) && current_chaincodes && 791: current_chaincodes[i].cur; i++) 792: { 793: printf("length: %d\n", Length(¤t_chaincodes[i])); 794: /*count the number of nodes in the chaincode. Unless the size is 795: considered long enough, skip it and move on.*/ 796: if(Length(¤t_chaincodes[i]) < DECENT_SIZED_OBJECT) 797: continue; 798: printf("here inside detect game for loop\n"); 799: /*since points 1 & 2 are at a diagnal, and 3 & 4 are at a diagnol, 800: then the dist between 2 and 4 & 1 and 3 should be close 801: in value.*/ 802: length2to4[i] = findDist(object_array[i].corner2.x, 803: object_array[i].corner2.y, 804: object_array[i].corner4.x, 805: object_array[i].corner4.y); 806: length1to3[i] = findDist(object_array[i].corner1.x, 807: object_array[i].corner1.y, 808: object_array[i].corner3.x, 809: object_array[i].corner3.y); 810: 811: /*the other side pair*/ 812: length1to4[i] = findDist(object_array[i].corner1.x, 813: object_array[i].corner1.y, 814: object_array[i].corner4.x, 815: object_array[i].corner4.y); 816: length2to3[i] = findDist(object_array[i].corner2.x, 817: object_array[i].corner2.y, 818: object_array[i].corner3.x, 819: object_array[i].corner3.y); 820: 821: /*diagnols... always will be 1 & 2 and 3 & 4*/ 822: length1to2[i] = findDist(object_array[i].corner1.x, 823: object_array[i].corner1.y, 824: object_array[i].corner2.x, 825: object_array[i].corner2.y); 826: length3to4[i] = findDist(object_array[i].corner3.x, 827: object_array[i].corner3.y, 828: object_array[i].corner4.x, 829: object_array[i].corner4.y); 830: 831: /*calculate percent errors for all edge (and diagnal) combinations*/ 832: error24to13[i] = compare_length(length2to4[i], length1to3[i]);/*op.side*/ 833: error14to23[i] = compare_length(length1to4[i], length2to3[i]); 834: error14to13[i] = compare_length(length1to4[i], length1to3[i]);/*1 crn.*/ 835: error23to24[i] = compare_length(length2to3[i], length2to4[i]); 836: error31to32[i] = compare_length(length1to3[i], length2to3[i]); 837: error41to42[i] = compare_length(length1to4[i], length2to4[i]); 838: error12to34[i] = compare_length(length1to2[i], length3to4[i]);/*diag.*/ 839: 840: /*average all of the error values together*/ 841: error_avg = ((error24to13[i] + error14to23[i] + error14to13[i] + 842: error23to24[i] + error31to32[i] + error41to42[i] + 843: error12to34[i]) / 7); 844: 845: printf("error avg: %f\n\n", error_avg); 846: /*determine if the current object is considered the most likely to 847: be the ttt board so far. Average of all the error##to##'s. 848: If the current is */ 849: if(ml_error > error_avg) 850: { 851: most_likely = i; 852: ml_error = error_avg; 853: } 854: } 855: 856: printf("Object %d is most likely the board with %f\%% error.\n", 857: most_likely, ml_error * 100); 858: 859: return most_likely; /*return the object number that is the board*/ 860: } 861: 862: coord search_moravec(coord point1, coord point2, PGMImage *moravec) 863: { 864: int i, j; /*loop counting*/ 865: float t; 866: coord temp; 867: float dist = findDist(point1.x, point1.y, point2.x, point2.y); 868: int search_size = 5; 869: 870: coord most_likely_corner = {-1, -1}; 871: int most_likely_sum = 0; 872: int sum_moravec_points = 0; 873: 874: /*calculate the coordinates where the divider edges should be.*/ 875: for(t = ONE_HALF; t > ONE_SIXTH; t -= (1 / dist)) 876: { 877: temp = find_dividers(point1, point2, t); 878: /* search_size = findDist(point1.x, point1.y, point2.x, point2.y) / 10;*/ 879: sum_moravec_points = 0; /*clear this before next iteration*/ 880: 881: for(i = -(search_size); i <= search_size; i++) 882: { 883: for(j = -(search_size); j <= search_size; j++) 884: { 885: if(rgb_avg((*moravec).data[temp.y + i][temp.x + j]) > 115) 886: { 887: /*setCPixel(temp.x + j, temp.y + i, red);*/ 888: sum_moravec_points++; 889: } 890: } 891: } 892: 893: /*if the current point in the search is the best, store it*/ 894: if(most_likely_sum <= sum_moravec_points) 895: { 896: most_likely_corner.x = temp.x; 897: most_likely_corner.y = temp.y; 898: most_likely_sum = sum_moravec_points; 899: } 900: } 901: 902: return most_likely_corner; 903: } 904: 905: /*anchor - the corner closest to the side divider point being looked for. 906: common - the corner that the line between itself and the anchor corner contains 907: the target divider point. 908: anchor_opposite - the corner adjacent to the anchor corner & oppsoite common. 909: common_opposite - the corner adjacent to the common corner & oppposite anchor. 910: */ 911: /*return the divider point*/ 912: coord find_side_points(coord anchor, coord common, coord anchor_opposite, 913: coord common_opposite, PGMImage *moravec) 914: { 915: coord temp1, temp2; 916: 917: temp1 = find_dividers(anchor, anchor_opposite, ONE_TWELTH); 918: temp2 = find_dividers(common, common_opposite, ONE_TWELTH); 919: 920: return search_moravec(temp1, temp2, moravec); 921: } 922: 923: coord find_intersection(coord line1_point1, coord line1_point2, 924: coord line2_point1, coord line2_point2) 925: { 926: float line1_slope, line2_slope; 927: coord target = {-1, -1}; 928: int *temp = NULL; 929: /*find slope for first line*/ 930: if((line1_point1.x - line1_point2.x) != 0) 931: { 932: line1_slope = ((float)(line1_point1.y - line1_point2.y)) / 933: ((float)(line1_point1.x - line1_point2.x)); 934: } 935: else /*otherwise handle the undefined slope*/ 936: { 937: /*find slope for secon line when first is undefined*/ 938: if((line2_point1.x - line2_point2.x) != 0) 939: { 940: line2_slope = ((float)(line2_point1.y - line2_point2.y)) / 941: ((float)(line2_point1.x - line2_point2.x)); 942: } 943: else /*this should never happen, but could if someone specifed the same 944: line twice*/ 945: return target; /*target is initalized to (-1, -1)*/ 946: 947: /*since the slope is undefined the x coord in known*/ 948: target.x = line1_point1.x; 949: target.y = line2_slope * target.x + line2_point1.y; /*y = mx + b*/ 950: printf("line one has undefined slope\n"); 951: return target; 952: } 953: 954: /*find slope for second line*/ 955: if((line2_point1.x - line2_point2.x) != 0) 956: { 957: line2_slope = ((float)(line2_point1.y - line2_point2.y)) / 958: ((float)(line2_point1.x - line2_point2.x)); 959: } 960: else 961: { 962: /*since the slope is undefined the x coord in known*/ 963: target.x = line1_point1.x; 964: target.y = line1_slope * target.x + line1_point1.y; /*y = mx + b*/ 965: printf("line two has undefined slope\n"); 966: return target; 967: } 968: 969: /*if both lines have defined slopes find the target point*/ 970: /* slope is m and defined by: 971: (y1 - y2) 972: m = --------- 973: (x1 - x2) 974: 975: target calculated by setting Yt = M1*(Xt - Xa) + Ya equal to 976: Yt = M2 * (Xt - Xc) + Yc to get 977: 978: (Xa*M1 - M2*Xc + Yc - Ya) 979: Xt = ------------------------- 980: M1 - M2 981: 982: then to get the y coordinate sub Xt into: 983: 984: Yt = M1 * (Xt - Xa) + Ya 985: 986: Where line1_point1 = a 987: line1_point2 = b 988: line2_point1 = c 989: line12point2 = d 990: and are indicated as subscripts of their X or Y coordinate. 991: M1 = line1_slope 992: M2 = line2_slope 993: */ 994: 995: 996: 997: target.x = (((float)(line1_point1.x * line1_slope) 998: - (float)(line2_point1.x * line2_slope) 999: + (float)line2_point1.y - (float)line1_point1.y)) 1000: / (float)(line1_slope - line2_slope); 1001: 1002: target.y = line1_slope * ((float)target.x - (float)line1_point1.x) 1003: + (float)line1_point1.y; 1004: /*target.y = line2_slope * ((float)target.x - (float)line2_point1.x) 1005: + (float)line2_point1.y;*/ 1006: 1007: /*printf("%d\n", *temp); *//*for gdb*/ 1008: return target; 1009: } 1010: 1011: 1012: /*find the ttt boards cross lines coordinates*/ 1013: void detect_ttt_board(object board_object, ttt *board_details, 1014: PGMImage *moravec) 1015: { 1016: /*copy the corners into the ttt datatype*/ 1017: memcpy(&(board_details->corners), &board_object, sizeof(object)); 1018: 1019: board_details->side13 = find_side_points(board_object.corner1, 1020: board_object.corner3, 1021: board_object.corner2, 1022: board_object.corner4, moravec); 1023: 1024: board_details->side14 = find_side_points(board_object.corner1, 1025: board_object.corner4, 1026: board_object.corner2, 1027: board_object.corner3, moravec); 1028: 1029: board_details->side23 = find_side_points(board_object.corner2, 1030: board_object.corner3, 1031: board_object.corner1, 1032: board_object.corner4, moravec); 1033: 1034: board_details->side24 = find_side_points(board_object.corner2, 1035: board_object.corner4, 1036: board_object.corner1, 1037: board_object.corner3, moravec); 1038: 1039: board_details->side31 = find_side_points(board_object.corner3, 1040: board_object.corner1, 1041: board_object.corner4, 1042: board_object.corner2, moravec); 1043: 1044: board_details->side32 = find_side_points(board_object.corner3, 1045: board_object.corner2, 1046: board_object.corner4, 1047: board_object.corner1, moravec); 1048: 1049: board_details->side41 = find_side_points(board_object.corner4, 1050: board_object.corner1, 1051: board_object.corner3, 1052: board_object.corner2, moravec); 1053: 1054: board_details->side42 = find_side_points(board_object.corner4, 1055: board_object.corner2, 1056: board_object.corner3, 1057: board_object.corner1, moravec); 1058: 1059: /*now that the sides are found, find the intersections to find the 1060: middle points*/ 1061: 1062: board_details->middle1 = find_intersection(board_details->side14, 1063: board_details->side32, 1064: board_details->side13, 1065: board_details->side42); 1066: 1067: board_details->middle2 = find_intersection(board_details->side23, 1068: board_details->side41, 1069: board_details->side24, 1070: board_details->side31); 1071: 1072: board_details->middle3 = find_intersection(board_details->side31, 1073: board_details->side24, 1074: board_details->side32, 1075: board_details->side14); 1076: printf("middle3: (%d, %d)\n", board_details->middle3.x, board_details->middle3.y); 1077: board_details->middle4 = find_intersection(board_details->side41, 1078: board_details->side23, 1079: board_details->side42, 1080: board_details->side13); 1081: 1082: } 1083: 1084: /*****Find the pieces*************************************************** 1085: ***********************************************************************/ 1086: 1087: /*Takes four points that must be in order going around the region. Also, 1088: needs a pointer to an image with the moravec filter applied.*/ 1089: int search_area(coord point1, coord point2, coord point3, coord point4, 1090: PGMImage *moravec) 1091: { 1092: /*The following variables are used to calculate the existence of a marble 1093: at a location on the board or not. Uses the following basic formula 1094: new_point = ((t-1) * 1st point) + (t * 2nd point) 1095: where the start & end coords are opposite sides of the area being 1096: searched. If you take the entire area of a single position on a ttt 1097: board, the area that is searched is the region that is bouned by the 1098: points halfway between those passed in to this function.*/ 1099: 1100: float t, s, r; /*hold values incrementing from 0 to 1*/ 1101: coord t_start, t_end, s_start, s_end; 1102: coord temp_t, temp_s, temp_r; 1103: float dist_t, dist_s, dist_r; 1104: 1105: /*pixel count of those over the threashhold and total pixels*/ 1106: int pixel_count = 0, moravec_count = 0; 1107: 1108: t_start = find_dividers(point1, point2, ONE_HALF); 1109: t_end = find_dividers(point1, point4, ONE_HALF); 1110: 1111: s_start = find_dividers(point3, point2, ONE_HALF); 1112: s_end = find_dividers(point3, point4, ONE_HALF); 1113: 1114: dist_t = findDist(t_start.x, t_start.y, t_end.x, t_end.y); 1115: dist_s = findDist(s_start.x, s_start.y, s_end.x, s_end.y); 1116: printf("%f %f\n", dist_t, dist_s); 1117: 1118: /*march two points along that parallel each other in the search area*/ 1119: for(t = 0.0, s = 0.0; t <= 1.0; t += (1.0 / dist_t), s += (1.0 / dist_s)) 1120: { 1121: temp_t = find_dividers(t_start, t_end, t); 1122: 1123: temp_s = find_dividers(s_start, s_end, s); 1124: 1125: dist_r = findDist(temp_t.x, temp_t.y, temp_s.x, temp_s.y); 1126: 1127: /*march a single point along that starts at temp_s and ends 1128: at temp_t. This fills in the region for the search.*/ 1129: for(r = 0.0; r <= 1.0; r += (1 / dist_r)) 1130: { 1131: temp_r = find_dividers(temp_s, temp_t, r); 1132: 1133: /*talley the number of edge points found (rgb. avg. >= 128)*/ 1134: if(rgb_avg((*moravec).data[temp_r.y][temp_r.x]) >= 128) 1135: { 1136: setCPixel(temp_r.x, temp_r.y, blue); 1137: moravec_count++; 1138: } 1139: pixel_count++; /*increment the number of pixels*/ 1140: } 1141: 1142: } 1143: 1144: printf("total: %d edge: %d\n", pixel_count, moravec_count); 1145: 1146: /*if the number of edge pixels is greater than 10% of the total, then 1147: this is an edge of marble. The percent is there because there could be 1148: a few small blobs that get counted when the shouldn't that otherwise 1149: would give a positive hit that a marble was found*/ 1150: if(moravec_count >= (pixel_count / 10)) 1151: return TRUE; 1152: 1153: /*otherwise a marble isn't here*/ 1154: return FALSE; 1155: } 1156: 1157: /*takes the object number that is determined to be the board*/ 1158: void detect_pieces(ttt *board_data, PGMImage* moravec) 1159: { 1160: if(search_area(board_data->corners.corner1, board_data->side13, 1161: board_data->middle1, board_data->side14, moravec)) 1162: printf("corner 1 has a piece\n"); 1163: else 1164: printf("corner 1 has no piece\n"); 1165: 1166: 1167: if(search_area(board_data->middle1, board_data->middle3, 1168: board_data->middle2, board_data->middle4, moravec)) 1169: printf("middle has a piece\n"); 1170: else 1171: printf("middle has no piece\n"); 1172: 1173: 1174: if(search_area(board_data->corners.corner2, board_data->side23, 1175: board_data->middle2, board_data->side24, moravec)) 1176: printf("corner 2 has a piece\n"); 1177: else 1178: printf("corner 2 has no piece\n"); 1179: 1180: setCPixel(board_data->middle1.x, board_data->middle1.y, red); 1181: setCPixel(board_data->middle2.x, board_data->middle2.y, green); 1182: setCPixel(board_data->middle3.x, board_data->middle3.y, blue); 1183: setCPixel(board_data->middle4.x, board_data->middle4.y, yellow); 1184: } 1185: 1186: 1187: /* ================================================================= 1188: * Callback functions. 1189: * 1190: * color = displayed graphics in window 1191: * menu = menu event handling 1192: * keyboard = deyboard event handling 1193: * ----------------------------------------------------------------- */ 1194: void color(void) 1195: { 1196: /*glClear (GL_COLOR_BUFFER_BIT);*/ 1197: 1198: /*show the current image*/ 1199: showColor(img_cur); 1200: 1201: /*show the current abstract information*/ 1202: showAbstract(all_objects, ttt_board); 1203: 1204: } 1205: 1206: void buffer() 1207: { 1208: /*if the drawing state of all objects is enabled, recalculate*/ 1209: if(draw_abstract_lines >= 0) 1210: draw_abstract_lines = detect_corners(chain_codes, all_objects); 1211: 1212: /*if the drawing state of the object most likely to be the board is 1213: found, recalulate*/ 1214: if(draw_abstract_board >= 0) 1215: draw_abstract_board = detect_game(chain_codes, all_objects); 1216: 1217: glutPostRedisplay(); 1218: } 1219: 1220: #define RESTART 0 1221: #define PERS_CORR 3 1222: #define COLOR_TO_GRAY 4 1223: #define MORAVEC 5 1224: #define EDGES 6 1225: #define CORNERS 7 1226: #define BUFFERS 8 1227: #define GAME 9 1228: #define PIECES 10 1229: 1230: /*this is not a callback function, but is used inside menu() for setting 1231: new states of execution*/ 1232: void reset_state(PGMImage* new_current) 1233: { 1234: img_cur = new_current; 1235: draw_abstract_lines = -1; 1236: draw_abstract_board = -1; 1237: free_chaincodes(&chain_codes); 1238: } 1239: 1240: void menu(int selection) 1241: { 1242: if(selection == RESTART) 1243: { 1244: reset_state(img_original); 1245: } 1246: if(selection == PERS_CORR) 1247: { 1248: pers_corr(img_pers_corr, img_cur); 1249: reset_state(img_pers_corr); 1250: } 1251: if(selection == COLOR_TO_GRAY) 1252: { 1253: color_to_gray(img_grayscale, img_cur); 1254: reset_state(img_grayscale); 1255: } 1256: if(selection == MORAVEC) 1257: { 1258: moravec(img_moravec, img_cur); 1259: reset_state(img_moravec); 1260: } 1261: if(selection == EDGES) 1262: { 1263: /*if there is a chaincode already in memory, free it before a new one 1264: is created.*/ 1265: showChain(img_cur, &chain_codes); 1266: } 1267: if(selection == CORNERS) 1268: { 1269: /*if there are chaincodes already in memory, free it before a new one 1270: is created.*/ 1271: showChain(img_cur, &chain_codes); 1272: 1273: draw_abstract_lines = detect_corners(chain_codes, all_objects); 1274: } 1275: if(selection == BUFFERS) 1276: { 1277: if(is_buffered == FALSE) 1278: { 1279: glutIdleFunc(buffer); 1280: is_buffered = TRUE; 1281: } 1282: else 1283: { 1284: glutIdleFunc(NULL); 1285: is_buffered = FALSE; 1286: } 1287: } 1288: if(selection == GAME) 1289: { 1290: /*need an image with the moravec filter applied*/ 1291: /*don't calculating this if it is the current image!!! 1292: Bad things happen like memseting to zero the current image.*/ 1293: if(img_cur != img_moravec) 1294: { 1295: moravec(img_moravec, img_cur); 1296: /*img_cur = img_moravec;*/ 1297: } 1298: 1299: /*if there are chaincodes already in memory, free it before a new one 1300: is created.*/ 1301: showChain(img_moravec, &chain_codes); 1302: 1303: detect_corners(chain_codes, all_objects); 1304: draw_abstract_board = detect_game(chain_codes, all_objects); 1305: detect_ttt_board(all_objects[draw_abstract_board], ttt_board, 1306: img_moravec); 1307: return; 1308: } 1309: if(selection == PIECES) 1310: { 1311: /*need an image with the moravec filter applied*/ 1312: /*don't calculating this if it is the current image!!! 1313: Bad things happen like memseting to zero the current image.*/ 1314: if(img_cur != img_moravec) 1315: { 1316: moravec(img_moravec, img_cur); 1317: /*img_cur = img_moravec;*/ 1318: } 1319: 1320: /*if there are chaincodes already in memory, they are freed before a 1321: new one is created.*/ 1322: showChain(img_moravec, &chain_codes); 1323: 1324: /*detect the ttt game board*/ 1325: detect_corners(chain_codes, all_objects); 1326: draw_abstract_board = detect_game(chain_codes, all_objects); 1327: detect_ttt_board(all_objects[draw_abstract_board], ttt_board, 1328: img_moravec); 1329: 1330: /*detect the locations of the pieces*/ 1331: detect_pieces(ttt_board, img_moravec); 1332: 1333: glFlush(); 1334: return; 1335: } 1336: 1337: glutPostRedisplay(); /*redraw the image*/ 1338: } 1339: 1340: void keyboard(unsigned char key, int x, int y) 1341: { 1342: switch (key) 1343: { 1344: case 27: 1345: exit(0); 1346: break; 1347: } 1348: } 1349: 1350: void mouse(int button, int state, int x, int y) 1351: { 1352: char temp[50]; 1353: 1354: if(button == GLUT_LEFT_BUTTON && state == GLUT_DOWN) 1355: { 1356: sprintf(temp, "(x, y): (%d, %d) red: %d green: %d blue: %d\n", 1357: x, VSIZE - y, (*img_cur).data[VSIZE - y][x].red, 1358: (*img_cur).data[VSIZE - y][x].green, 1359: (*img_cur).data[VSIZE - y][x].blue); 1360: setCRect(0, 0, 200, 12, black); 1361: glColor3f(1.0, 0.0, 0.0); 1362: drawString(0, 0, GLUT_BITMAP_TIMES_ROMAN_10, temp); 1363: glFlush(); 1364: } 1365: } 1366: 1367: 1368: /* ================================================================= 1369: * init() & parse_command_line() 1370: * initalize none-OpenGL related things. 1371: * ----------------------------------------------------------------- */ 1372: 1373: /*set global flag variables from things specified at commandline.*/ 1374: /*return the filename specified, if none specified exit().*/ 1375: char* parse_command_line(int argc, char** argv) 1376: { 1377: /*parse the command line*/ 1378: if(argc == 1) 1379: { 1380: printf("To few parameters.\n"); 1381: printf("Usage: research <file.pgm>\n"); 1382: exit(1); 1383: } 1384: else if(argc == 2) 1385: { 1386: return argv[1]; 1387: } 1388: else 1389: { 1390: printf("To many parameters.\n"); 1391: printf("Usage: research <file.pgm>\n"); 1392: exit(1); 1393: } 1394: } 1395: 1396: char* init (int argc, char** argv) 1397: { 1398: char* PGMfileName;/*pointer to the string containing the filename*/ 1399: 1400: /*parse the command line*/ 1401: PGMfileName = parse_command_line(argc, argv); 1402: 1403: /* 1404: * Read in image file. - note: sets our global values, too. 1405: * ----------------------------------------------------------------- */ 1406: 1407: /*allocate memory for original image*/ 1408: img_original = (PGMImage*) malloc(sizeof(PGMImage)); 1409: getPGMfile(PGMfileName, img_original); 1410: HSIZE = (*img_original).width; 1411: VSIZE = (*img_original).height; 1412: MVAL = (*img_original).maxVal; 1413: 1414: img_cur = img_original; /*VERY IMPORTANT to set this*/ 1415: 1416: /*allocate memory for pers. corr. image*/ 1417: img_pers_corr = (PGMImage*) malloc(sizeof(PGMImage)); 1418: (*img_pers_corr).width = HSIZE; 1419: (*img_pers_corr).height = VSIZE; 1420: (*img_pers_corr).maxVal = 255; 1421: 1422: img_grayscale = (PGMImage*) malloc(sizeof(PGMImage)); 1423: (*img_grayscale).width = HSIZE; 1424: (*img_grayscale).height = VSIZE; 1425: (*img_grayscale).maxVal = 255; 1426: 1427: img_moravec = (PGMImage*) malloc(sizeof(PGMImage)); 1428: (*img_moravec).width = HSIZE; 1429: (*img_moravec).height = VSIZE; 1430: (*img_moravec).maxVal = 255; 1431: 1432: 1433: all_objects = (object*) malloc(sizeof(object) * MAX_CHAINS); 1434: memset(all_objects, 0, sizeof(object) * MAX_CHAINS); 1435: 1436: ttt_board = (ttt*) malloc(sizeof(ttt)); 1437: memset(ttt_board, 0, sizeof(ttt)); 1438: 1439: return PGMfileName; 1440: } 1441: 1442: 1443: int main(int argc, char** argv) 1444: { 1445: char* PGMfileName; 1446: int WindowID; /*unique window id, there is only one used*/ 1447: int i; /*looping variable*/ 1448: 1449: /* 1450: * Call our init function to define non-OpenGL related things. 1451: * Have init return a pointer to the filename, used to display the 1452: * filename in the titlebar of the window. 1453: * ----------------------------------------------------------------- */ 1454: PGMfileName = init (argc, argv); 1455: 1456: /* 1457: * Initialize the glut package. 1458: * ----------------------------------------------------------------- */ 1459: glutInit(&argc, argv); 1460: glutInitDisplayMode (GLUT_SINGLE | GLUT_RGB); 1461: 1462: /* 1463: * Define a new window (its size, position and title). 1464: * ----------------------------------------------------------------- */ 1465: glutInitWindowSize (HSIZE, VSIZE); /*size*/ 1466: glutInitWindowPosition (10, 10); /*position*/ 1467: WindowID = glutCreateWindow (PGMfileName); /*title*/ 1468: glutSetWindow(WindowID); 1469: glutDisplayFunc(color); 1470: 1471: /* 1472: * select clearing color - white 1473: */ 1474: glClearColor (1.0, 1.0, 1.0, 0.0); 1475: 1476: /* 1477: * initialize viewport values 1478: */ 1479: glMatrixMode(GL_PROJECTION); 1480: glLoadIdentity(); 1481: glOrtho(-1.0, 1.0, -1.0, 1.0, -1.0, 1.0); 1482: 1483: /*add menus*/ 1484: glutCreateMenu(menu); 1485: glutAddMenuEntry("Restart", RESTART); 1486: glutAddMenuEntry("perspective correction", PERS_CORR); 1487: glutAddMenuEntry("color to gray", COLOR_TO_GRAY); 1488: glutAddMenuEntry("moravec", MORAVEC); 1489: glutAddMenuEntry("edges", EDGES); 1490: glutAddMenuEntry("corners", CORNERS); 1491: glutAddMenuEntry("detect game", GAME); 1492: glutAddMenuEntry("detect pieces", PIECES); 1493: glutAddMenuEntry("toggle buffering", BUFFERS); 1494: glutAttachMenu(GLUT_RIGHT_BUTTON); 1495: 1496: glutMouseFunc(mouse); 1497: glutKeyboardFunc(keyboard); 1498: glutMainLoop(); 1499: 1500: /* 1501: * When we reach here, we've left the event loop and are ready to 1502: * exit. 1503: * ----------------------------------------------------------------- */ 1504: return 0; 1505: } 1506: