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