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