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