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