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: