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