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