reference/ocr-new/Component.cc
changeset 0 6b8091ca909a
equal deleted inserted replaced
-1:000000000000 0:6b8091ca909a
       
     1 
       
     2 #include "system.h"
       
     3 #include "Component.h"
       
     4 #include "BitMap.h"
       
     5 #include <assert.h>
       
     6 #include "list.h"
       
     7 #include "tcl_interface.h"
       
     8 
       
     9 /*** Component.cc
       
    10   Member functions for Components 
       
    11   Component functions defined in Component.h
       
    12   rev 12/9/95 KM
       
    13 ***/
       
    14 
       
    15 extern Page * global_page;
       
    16 extern Page * active_page;
       
    17 
       
    18 
       
    19 Components::Components()
       
    20 :List()
       
    21 {
       
    22 }
       
    23 
       
    24 
       
    25 Components::~Components()
       
    26 {
       
    27 
       
    28   for (ListElement *ptr = first; ptr != NULL && ptr->item!=NULL; 
       
    29        ptr = ptr->next) {
       
    30     if (ptr->item != NULL)
       
    31       delete (Component *) (ptr->item);
       
    32      }
       
    33   while(!IsEmpty())
       
    34     Remove();
       
    35 }
       
    36 
       
    37 Component * Components::compAt(Point p)
       
    38 /*--------------------------------------------------------------
       
    39 Primary Purpose:  Return smallest component that contains this point
       
    40 Arguments:  Point of request
       
    41 Return Value: Component pointer or NULL if there is no component here
       
    42 Rev 4/22/96
       
    43 ---------------------------------------------------------------*/
       
    44 {
       
    45   double size;
       
    46   Component * returnComp = NULL;
       
    47 
       
    48   int rowwidth = (((Component *) last->item)->ul().x()) -
       
    49     (((Component *) first->item)->ul().x()) ;
       
    50 
       
    51   int rowheight = (((Component *) last->item)->ul().x()) -
       
    52     (((Component *) first->item)->ul().x());
       
    53  
       
    54 
       
    55     size = rowwidth*rowheight; // this is now as big as the whole line.
       
    56  
       
    57    for (ListElement *ptr = first; ptr != NULL && ptr->item!=NULL; 
       
    58        ptr = ptr->next) 
       
    59      {
       
    60        Component * item = (Component *) ptr->item;
       
    61        if ( p > item->ul() && p < item->lr())
       
    62 	 {
       
    63 	   double area = item->area();
       
    64 	   if(area < size) size = area;
       
    65 	   returnComp = item;
       
    66 	 }
       
    67        }
       
    68    return returnComp;
       
    69 
       
    70 }
       
    71 
       
    72 
       
    73 
       
    74 Component * Component::copy()
       
    75 {
       
    76   Component * newcomp = new Component(ful, flr);
       
    77   newcomp->numBits() = fnumBits;
       
    78   newcomp->fasciiId = new char[strlen(fasciiId)];
       
    79   strcpy(newcomp->fasciiId, fasciiId);
       
    80   newcomp->fconfid = confid();
       
    81   newcomp->charGroup = charGroup;
       
    82   newcomp->ffontId = fontId();
       
    83   for (int i = 0; i < numProperties; i++)
       
    84 	         newcomp->property(i) = fproperty[i];
       
    85   return newcomp;
       
    86 }
       
    87 
       
    88 
       
    89 
       
    90 int Component::AddToComponent(ListElement* intrvl, RLEMap* rlemap, 
       
    91 			      int horizMerge)
       
    92 {
       
    93   assert(intrvl != NULL);
       
    94   List* list = new List();           //make a new queue
       
    95   ListElement* current;
       
    96   ListElement* nextelt;
       
    97   int counter = 0;
       
    98   int currentRow;
       
    99 
       
   100   if (intrvl->previous != NULL)
       
   101     intrvl->previous->next = intrvl->next;
       
   102   else rlemap->fMapData[((RLEPair *) intrvl->item)->row]->first = intrvl->next;
       
   103   if (intrvl->next != NULL)
       
   104     intrvl->next->previous = intrvl->previous;
       
   105   list->first = intrvl;              //put starting interval on queue
       
   106   list->last = intrvl;
       
   107   list->length = 1;
       
   108   intrvl->next = NULL;
       
   109   intrvl->previous = NULL;
       
   110   currentRow = 0;
       
   111   while ((intrvl = list->first) != NULL && 
       
   112 	 currentRow < rlemap->imageLength()-1) //Take an interval off queue
       
   113     {
       
   114       currentRow = ((RLEPair *) intrvl->item)->row;
       
   115 
       
   116       for (int i=-1; i < 2; i+=2) { 
       
   117 	current = rlemap->fMapData[currentRow+i]->first;
       
   118 	while ((current != NULL) 
       
   119 	       && (((RLEPair *) current->item)->start <= 
       
   120 		   ((RLEPair *) intrvl->item)->end+horizMerge)) {
       
   121 
       
   122 //	  printf("Looking at an interval on row %d that goes from %d to %d\n",
       
   123 //		 currentRow, ((RLEPair *) intrvl->item)->start,
       
   124 //		  ((RLEPair *) intrvl->item)->end);
       
   125 
       
   126 	  if ((((RLEPair *) current->item)->end 
       
   127 	       >= ((RLEPair *) intrvl->item)->start-1) 
       
   128 	      && (((RLEPair *) current->item)->start <= 
       
   129 		  ((RLEPair *) intrvl->item)->end+horizMerge)) {
       
   130 //	  printf("Adding connection for interval on row %d that goes from %d to %d\n", currentRow+i,
       
   131 //		 ((RLEPair *) current->item)->start,
       
   132 //		  ((RLEPair *) current->item)->end);
       
   133 	
       
   134 	    if (current->previous != NULL)
       
   135 	      current->previous->next = current->next;   //take off RLEMap
       
   136 	    else
       
   137 	      rlemap->fMapData[currentRow+i]->first = current->next;
       
   138 	    if (current->next != NULL)
       
   139 	      current->next->previous = current->previous;
       
   140 	    nextelt = current->next;
       
   141 	    list->last->next = current;                //add to queue
       
   142 	    current->previous = list->last;
       
   143 	    list->last = current;
       
   144 	    current->next = NULL;
       
   145 	    current = nextelt;
       
   146 	    list->length++;
       
   147 	  } else
       
   148 	    current = current->next;
       
   149 	}
       
   150       }
       
   151       
       
   152       if ((((RLEPair *) intrvl->item)->start < ful.x()) || (ful.x()==-1)) {
       
   153 	ful.x() = ((RLEPair *) intrvl->item)->start;
       
   154 //	printf("Changed ful.x to %d\n", ful.x());
       
   155       }
       
   156       if ((((RLEPair *) intrvl->item)->end > flr.x()) || (flr.x()==-1)) {
       
   157 	flr.x() = ((RLEPair *) intrvl->item)->end;
       
   158 //	printf("Changed flr.x to %d\n", flr.x());
       
   159       }
       
   160       if ((((RLEPair *) intrvl->item)->row < ful.y()) || (ful.y()==-1)) {
       
   161 	ful.y() = ((RLEPair *) intrvl->item)->row;
       
   162 //	printf("Changed ful.y to %d\n", ful.y());
       
   163       }
       
   164       if ((((RLEPair *) intrvl->item)->row > flr.y()) || (flr.y()==-1)) {
       
   165 	flr.y() = ((RLEPair *) intrvl->item)->row;
       
   166 //	printf("Changed flr.y to %d\n", flr.y());
       
   167       }      
       
   168       list->first = intrvl->next;
       
   169       if (intrvl->next != NULL)
       
   170 	intrvl->next->previous = NULL;
       
   171       delete ((RLEPair *) (intrvl->item));
       
   172       delete intrvl;               //so the letter O won't go forever
       
   173       counter++;
       
   174       list->length--;
       
   175     }
       
   176 
       
   177   delete list;
       
   178   return counter;
       
   179 
       
   180 }
       
   181 
       
   182 void Component::setProperties(BitMap * map)  // was BitMap
       
   183 /*--------------------------------------------------------------
       
   184 Primary Purpose: Set the property vector for this component
       
   185 Arguments:  The BitMap to which this component belongs
       
   186 Return Value:
       
   187 Effects: The component is divided into a 5 by 5 grid.  A gray
       
   188     scale (0 - 255) for each section is determined.  The gray scale
       
   189     is 0 for all white, 255 for all black but normally will e somewhere
       
   190     between the two.  The gray scales are represented in properties
       
   191     0-24. 
       
   192     Property 25 is the grayscale accross the top.
       
   193     Property 26 is the grayscale accross the bottom.
       
   194     Property 27 is the width/height ratio again scaled to (0-255)
       
   195     Actually the formula for property 27 is
       
   196         width/ height * 255  if height > width
       
   197         1- height/width * 255 if width > height
       
   198     This way near 0 is very tall and thin
       
   199              near 128 height near width
       
   200 	     near 255 very wide
       
   201     Property 28 is  Indicator of a vertically disjoint character
       
   202      like i and j.
       
   203     Also the total number of black pixels is set in fnumBits.
       
   204     // This is not used at this time.    
       
   205 Constraints: The data fields ful and flr must already be set 
       
   206    before calling this function. These fields specify a bounding
       
   207    box for the character within the BitMap.
       
   208 Rev: 12/9 KM
       
   209 ---------------------------------------------------------------*/
       
   210 {
       
   211   if (ful > flr) 
       
   212     printf("Problem\n");
       
   213   assert (ful <= flr);
       
   214   short int hflag[NumHorizDiv + 1];  // flags horizontal section dividers
       
   215   short int vflag[NumVertDiv + 1];   // flags vertical section dividers
       
   216   float height, width;
       
   217   int propNum;
       
   218   float darkest = 0;
       
   219   float lightest; 
       
   220   int darkrow = 0;
       
   221   int lightrow = 0;
       
   222 
       
   223   Point sectionLr, sectionUl;
       
   224   // Set Number of bits
       
   225   fnumBits = map->pixelsInRegion(ful, flr);
       
   226 
       
   227   setSectionFlags(hflag, vflag);
       
   228   for (int r = 0; r < NumVertDiv; r++)
       
   229     for (int c = 0; c < NumHorizDiv; c++)
       
   230       {
       
   231 	propNum = (r * NumHorizDiv) + c;
       
   232 	sectionUl = Point(hflag[c], vflag[r]);
       
   233 	sectionLr = Point(hflag[c+1]-1, vflag[r+1]-1);
       
   234 	if (sectionUl <= sectionLr)
       
   235 	  fproperty[propNum] = map->grayScale(sectionUl, sectionLr); 
       
   236 	assert(fproperty[propNum] >= 0 && fproperty[propNum] < 256);
       
   237       }
       
   238 
       
   239   // set the height/width ratio
       
   240   // 0 is very thin 128 is even 256 is very wide.
       
   241   width = flr.x() - ful.x() + 1;
       
   242   height = flr.y() - ful.y() + 1;
       
   243  
       
   244   // Grayscale across the top - Indicator of top bar
       
   245   sectionUl = Point(ful.x(), ful.y());
       
   246   sectionLr = Point(flr.x(), ful.y() + (int)(height/(NumVertDiv*2)));
       
   247   fproperty[25] = map->grayScale(sectionUl, sectionLr);
       
   248 
       
   249    // Grayscale across bottom - Indicator of a foot for l opposed to 1
       
   250   sectionUl = Point(ful.x(),  flr.y() -  (int)(height/(NumVertDiv*2)));
       
   251   sectionLr = Point(flr.x(),  flr.y());
       
   252   fproperty[26] = map->grayScale(sectionUl, sectionLr);
       
   253 
       
   254   float hdivw = (float)height/width;
       
   255   float wdivh = (float) width/height;
       
   256   if (width > height)
       
   257        fproperty[27]= (short int) ((1- hdivw/2)*255);
       
   258   else
       
   259     fproperty[27] = (short int)((wdivh/2)* 255);
       
   260 
       
   261   // is this a disjoint character like i or j  255 = yes 0 = no
       
   262   fproperty[28]=0;
       
   263   lightest = width;
       
   264   for(int row = ful.y(); row < flr.y(); row++)
       
   265       {
       
   266 	int pixelsThisRow = pixelsBetween(map->row(row), ful.x(), flr.x());
       
   267 	if(!(pixelsThisRow))
       
   268 	      fproperty[28]=255;
       
   269       }
       
   270 
       
   271   fproperty[29]= 0;
       
   272     for(int p = 0; p < numProperties; p++)	
       
   273       assert(fproperty[p] >= 0 && fproperty[p] < 256);
       
   274 
       
   275 
       
   276 }
       
   277 
       
   278 void Component::setSectionFlags(short int hflag[], short int vflag[])
       
   279 /*--------------------------------------------------------------
       
   280 Primary Purpose: Breaks this component into a grid NumHorizDiv X NumVertDiv
       
   281     for determining grayscale property vectors.
       
   282 Arguments:  hflag[] is an empty array to be filled by this procedure with
       
   283     the starting columns of each horizontal subdivision.  vflag[] will
       
   284     be filled with the vertical subdivisions.
       
   285 Effects:  fills hflag[] with the starting column for each subdivision.
       
   286      The last element of the array is actually the pixel immediately 
       
   287      following the last  subdivision.  The last subdivision contains any 
       
   288      remaining pixels that did not divide evenly amongst the divisions.
       
   289      vflag[NumHorizDiv] is comparable for vertical supdivisions.
       
   290      Example ful = (0,25) flr = (52,46) NumHorizDiv = NumVertDiv = 5
       
   291          hflag[6] = { 0,10,20,30,40,53 }
       
   292 	 vflag[6] = {25.29.33.37.41.47 } 
       
   293 Constraints: ful and flr must be set to mark the bounding box before
       
   294    calling this procedure.
       
   295 Rev:  10/27 KM
       
   296 ---------------------------------------------------------------*/
       
   297 {
       
   298   int ulx = ful.x();  int uly = ful.y();
       
   299   int lrx = flr.x();  int lry = flr.y();
       
   300 
       
   301   int width =  lrx - ulx+1;
       
   302   int height = lry - uly+1;
       
   303 
       
   304   int horizDiv = width/NumHorizDiv;
       
   305   int vertDiv = height/NumVertDiv;
       
   306 
       
   307   int horizExtra = width - horizDiv*NumHorizDiv;
       
   308   int vertExtra = height - vertDiv*NumVertDiv;
       
   309 
       
   310   int i, add;
       
   311   for (i = 0; i < NumHorizDiv; i++)
       
   312     {
       
   313     if(horizExtra - i > 0) add = i; else add = horizExtra;
       
   314     hflag[i] = ulx + (i*horizDiv)+ add;
       
   315   }
       
   316   hflag[i] = lrx + 1;              // Closes off last division
       
   317 
       
   318   int j;
       
   319   for(j = 0; j < NumVertDiv; j ++)
       
   320       {
       
   321     if(vertExtra - j > 0) add = j; else add = vertExtra;
       
   322     vflag[j] = uly + (j*vertDiv)+ add;
       
   323   }
       
   324   vflag[j] = lry + 1;
       
   325 
       
   326     
       
   327 
       
   328 
       
   329 }
       
   330 
       
   331 
       
   332 Distance Component::distance(Component * comp) 
       
   333 /*--------------------------------------------------------------
       
   334 Primary Purpose: Determines heuristic distance between two components
       
   335 Arguments:  Another component to compare
       
   336 Return Value: integer value which represents the distance between two 
       
   337    components. Distance = sum over i of
       
   338                        weight *square (this->fproperty[i] - comp->fproperty[i])
       
   339 		       weight for i == 26, 27 is 3 weight is 1 for all other 
       
   340 		       properties
       
   341 Constraints: setProperties must have been run on both components
       
   342 
       
   343 Rev: 11/1 KM
       
   344 ---------------------------------------------------------------*/
       
   345 {
       
   346   Property * a = fproperty;
       
   347   Property * b = comp->properties();
       
   348   Distance dist=0;
       
   349   int dif=0;
       
   350   int worst = 0;
       
   351   int weight = 1;
       
   352 
       
   353   for(int i= 0; i < numProperties; i++)
       
   354     { 
       
   355 
       
   356       if(i == 27) weight = 50;
       
   357      if (i == 28) weight = 3;
       
   358      else if(i != 27 && i != 28)
       
   359        weight = 1;
       
   360      
       
   361        dif = (a[i] - b[i]);      
       
   362       dist += weight*dif*dif;
       
   363     }
       
   364 
       
   365   return dist;
       
   366 }
       
   367 
       
   368 
       
   369 
       
   370 
       
   371 
       
   372 
       
   373 
       
   374 
       
   375 
       
   376 void printVector(short int vector[], int size)
       
   377 {
       
   378   for (int i = 0; i < size; i++)
       
   379     cout << vector[i] << " " ;
       
   380   cout << endl;
       
   381 
       
   382 }
       
   383 
       
   384 
       
   385 void testProperties(Component* c, BitMap * map)
       
   386 {
       
   387   short int hflag[NumHorizDiv + 1];  // flags horizontal section dividers
       
   388   short int vflag[NumVertDiv + 1];   // flags vertical section dividers
       
   389 
       
   390   cout << "First test subDivisions  " << endl;
       
   391   c->setSectionFlags(hflag, vflag);
       
   392   cout << "Horizontal flags" <<endl;
       
   393   printVector(hflag, NumHorizDiv + 1);
       
   394   cout << "Vertical flags" <<endl;
       
   395   printVector(vflag, NumHorizDiv + 1);
       
   396 
       
   397   cout << "Now lets look at the properties " << endl;
       
   398   // setSectionFlags will actually get called again within setProperties
       
   399   c->setProperties(map);
       
   400   printVector(c->properties(), NumHorizDiv*NumVertDiv + 1);
       
   401   cout << endl << " The distance of this component from itself: " << " ";
       
   402   cout << c->distance(c) << endl;
       
   403 
       
   404 }
       
   405 
       
   406 void Component::display_bounding_box()
       
   407 {
       
   408   display_bounding_box("blue");
       
   409 }
       
   410 
       
   411 void Component::display_bounding_box(char * color)
       
   412 {
       
   413   if(global_page == active_page)
       
   414     {
       
   415       display_bounding_box(color, SCALE_FACTOR, 
       
   416 		       ".main_window.display.work_space");
       
   417     }
       
   418 }
       
   419 
       
   420 void Component::display_bounding_box(char * color, double scaleFactor, 
       
   421 				     char * window)
       
   422 {
       
   423   int ulx = (ul()).x();
       
   424   int uly = (ul()).y();
       
   425   int lrx = (lr()).x();
       
   426   int lry = (lr()).y();
       
   427   scale(ulx,scaleFactor); scale(uly,scaleFactor); scale(lrx,scaleFactor); 
       
   428   scale(lry,scaleFactor);
       
   429   
       
   430   if(ENABLE_USER_INTERFACE)
       
   431     docommand("%s create rectangle %d %d %d %d -outline %s -tags IMAGE_TAG ",  window, ulx, uly, lrx, lry, color);
       
   432 }  
       
   433 
       
   434 
       
   435 Distance Component::recognize(Component * learnedchars)
       
   436 // This is out of date. Current recognize is below
       
   437 {
       
   438   Distance d, nextd;
       
   439   char id;
       
   440 
       
   441 
       
   442 //  printf("Another call to recognize\n");
       
   443   d = (256*256)*numProperties;  // this is the biggest distance
       
   444                         
       
   445 
       
   446 	       
       
   447     for (int i = 0; i < 256; i++)
       
   448 	{
       
   449 	  if(learnedchars[i].confid() != 0)
       
   450 	      {
       
   451 		nextd = distance(&learnedchars[i]);
       
   452 //		printf("Distance = %d, character = %c\n", nextd, i);
       
   453 		if (nextd < d)
       
   454 		    {
       
   455 		      d = nextd;
       
   456 		      id = (char) i;
       
   457 		    }
       
   458 	      }
       
   459 	  
       
   460 	}
       
   461   
       
   462   fasciiId = &id;
       
   463   /*  printf("Recognized a Component: %c\n", id); */
       
   464   return d;
       
   465 }
       
   466 
       
   467 
       
   468 Distance Component::recognize(Components * learnedgroups, bool allGroups)
       
   469 {
       
   470   Distance d, worstDistance,nextd;
       
   471   char * id;
       
   472   short int fontid;
       
   473   float tempd;
       
   474   worstDistance = 150000;
       
   475 
       
   476 //  printf("Another call to new recognize\n");
       
   477   d = (65536)*numProperties;  // this is the biggest distance
       
   478   
       
   479                         
       
   480     fconfid = 0;
       
   481 
       
   482     for(int g = 0; g < NumCharGroups &&
       
   483  	((fconfid < ConfidenceThreshold) || allGroups); g++)
       
   484     {
       
   485       int offset = (charGroup+g) % NumCharGroups;
       
   486       //      if (offset == 4 && charGroup != 4) continue;
       
   487       for (ListElement* ptr = learnedgroups[offset].first; ptr != NULL;
       
   488 	 ptr = ptr->next)
       
   489 	{
       
   490 	  Component * item = (Component *) ptr->item;
       
   491 	  nextd = distance(item);
       
   492 //	  printf("Distance = %d, character = %c\n", nextd, i);
       
   493 	  if (nextd < d)
       
   494 	      {
       
   495 		d = nextd;
       
   496 		id = item->fasciiId;
       
   497 		fontid = item->ffontId;
       
   498 	      }
       
   499 
       
   500 	}
       
   501 
       
   502       if (d >= worstDistance) 
       
   503 	  tempd = worstDistance - 1;
       
   504       else tempd = d;
       
   505 	
       
   506 	fconfid = (unsigned short int)
       
   507 	  (255 - (tempd/worstDistance)*256);
       
   508       if(charGroup == 4) break;  // dont check other groups for floaters
       
   509     }
       
   510   
       
   511 
       
   512   fasciiId = new char[strlen(id)+1];
       
   513   strcpy(fasciiId,id);
       
   514 
       
   515 
       
   516   ffontId = fontid;
       
   517   //printf("Recognized a Component: %s confid-%d dist-%u h/wratio %u\n", 
       
   518   //	 id,  fconfid, d, property(27));
       
   519 
       
   520 
       
   521 
       
   522   return d;
       
   523 }
       
   524 
       
   525 
       
   526 
       
   527 int Component::vertShrink(BitMap * bitmap)
       
   528 {
       
   529   int r;
       
   530   int shrunk = 0;
       
   531 	for(r = ful.y(); r < flr.y(); r++)
       
   532 	      if (pixelsBetween(bitmap->row(r), ful.x(), flr.x()))
       
   533 		  {
       
   534 		    ful.y() = r;
       
   535 		    shrunk = 1;
       
   536 		    break;
       
   537 		  }
       
   538 	for(r = flr.y(); r > ful.y(); r--)
       
   539 	      if (pixelsBetween(bitmap->row(r), ful.x(), flr.x()))
       
   540 		  {
       
   541 		    flr.y() = r;
       
   542 		    shrunk = 1;
       
   543 		    break;		    
       
   544 		  }
       
   545 		
       
   546   return shrunk;
       
   547 }
       
   548 
       
   549 
       
   550 int Component::horizontalShrink(BitMap * bitmap)
       
   551 {
       
   552   int c;
       
   553   int shrunk = 0;
       
   554 	for(c = ful.x(); c < flr.x(); c++)
       
   555 	      if (bitmap->pixelsInRegion( Point(c, ful.y()), 
       
   556 					  Point(c, flr.y())))
       
   557 		  {
       
   558 		    if (ful.x() != c)
       
   559 		      shrunk = 1;
       
   560 		    ful.x() = c;
       
   561 		    break;
       
   562 		  }
       
   563 
       
   564 	// now start from the other side
       
   565 	for(c = flr.x(); c > ful.x(); c--)
       
   566 	      if (bitmap->pixelsInRegion( Point(c, ful.y()), 
       
   567 					  Point(c, flr.y())))
       
   568 		  {
       
   569 		    if (flr.x() != c)
       
   570 		      shrunk = 1;
       
   571 		    flr.x() = c;
       
   572 		    break;
       
   573 		  }
       
   574 
       
   575 		
       
   576   return shrunk;
       
   577 }
       
   578 
       
   579 
       
   580 
       
   581 
       
   582 
       
   583 
       
   584 void Component::join(Component * comp)
       
   585 {
       
   586 
       
   587   display_bounding_box("white");
       
   588   comp->display_bounding_box("white");
       
   589 
       
   590   ful.x() = ((ful.x() < comp->ul().x()) ? ful.x() : comp->ul().x());
       
   591   ful.y() = ((ful.y() < comp->ul().y()) ? ful.y() : comp->ul().y());
       
   592 
       
   593   flr.x() = ((flr.x() > comp->lr().x()) ? flr.x() : comp->lr().x());
       
   594   flr.y() = ((flr.y() > comp->lr().y()) ? flr.y() : comp->lr().y());
       
   595   display_bounding_box("blue");
       
   596 
       
   597 }
       
   598 
       
   599 
       
   600 
       
   601 
       
   602 
       
   603 
       
   604 
       
   605 
       
   606 
       
   607