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