reference/ocr-new/properties.doc
author viric@llimona
Thu, 18 May 2006 23:12:51 +0200
changeset 0 6b8091ca909a
permissions -rw-r--r--
Init from working directory of svn repository.

#include "system.h"
#include "Component.h"
#include "BitMap.h"
#include <assert.h>
#include "list.h"
#include "tcl_interface.h"

/*** Component.cc
  Member functions for Components 
  Component functions defined in Component.h
  rev 12/9/95 KM
***/


Components::Components()
:List()
{
}


Components::~Components()
{
  for (ListElement *ptr = first; ptr != NULL && ptr->item!=NULL; 
       ptr = ptr->next) {
    if (ptr->item != NULL)
      delete (Component *) (ptr->item);
     }
  while(!IsEmpty())
    Remove();

}


int Component::AddToComponent(ListElement* intrvl, RLEMap* rlemap)
//this needs to be fixed to trap page boundaries
//or else pad the page with a blank line at top and bottom
{
  assert(intrvl != NULL);
  List* list = new List();           //make a new queue
  ListElement* current;
  ListElement* nextelt;
  int counter = 0;
  int currentRow;

  if (intrvl->previous != NULL)
    intrvl->previous->next = intrvl->next;
  else rlemap->fMapData[((RLEPair *) intrvl->item)->row]->first = intrvl->next;
  if (intrvl->next != NULL)
    intrvl->next->previous = intrvl->previous;
  list->first = intrvl;              //put starting interval on queue
  list->last = intrvl;
  list->length = 1;
  intrvl->next = NULL;
  intrvl->previous = NULL;
  currentRow = 0;
  while ((intrvl = list->first) != NULL && 
	 currentRow < rlemap->imageLength()) //Take an interval off queue
    {
      currentRow = ((RLEPair *) intrvl->item)->row;
      for (int i=-1; i < 2; i+=2) { 
	current = rlemap->fMapData[currentRow+i]->first;
	while ((current != NULL) 
	       && (((RLEPair *) current->item)->start <= 
		   ((RLEPair *) intrvl->item)->end+MinHorizSeparation)) {

//	  printf("Looking at an interval on row %d that goes from %d to %d\n",
//		 currentRow, ((RLEPair *) intrvl->item)->start,
//		  ((RLEPair *) intrvl->item)->end);

	  if ((((RLEPair *) current->item)->end 
	       >= ((RLEPair *) intrvl->item)->start-1) 
	      && (((RLEPair *) current->item)->start <= 
		  ((RLEPair *) intrvl->item)->end+MinHorizSeparation)) {
//	  printf("Adding connection for interval on row %d that goes from %d to %d\n", currentRow+i,
//		 ((RLEPair *) current->item)->start,
//		  ((RLEPair *) current->item)->end);
	
	    if (current->previous != NULL)
	      current->previous->next = current->next;   //take off RLEMap
	    else
	      rlemap->fMapData[currentRow+i]->first = current->next;
	    if (current->next != NULL)
	      current->next->previous = current->previous;
	    nextelt = current->next;
	    list->last->next = current;                //add to queue
	    current->previous = list->last;
	    list->last = current;
	    current->next = NULL;
	    current = nextelt;
	    list->length++;
	  } else
	    current = current->next;
	}
      }
      
      if ((((RLEPair *) intrvl->item)->start < ful.x()) || (ful.x()==-1)) {
	ful.x() = ((RLEPair *) intrvl->item)->start;
//	printf("Changed ful.x to %d\n", ful.x());
      }
      if ((((RLEPair *) intrvl->item)->end > flr.x()) || (flr.x()==-1)) {
	flr.x() = ((RLEPair *) intrvl->item)->end;
//	printf("Changed flr.x to %d\n", flr.x());
      }
      if ((((RLEPair *) intrvl->item)->row < ful.y()) || (ful.y()==-1)) {
	ful.y() = ((RLEPair *) intrvl->item)->row;
//	printf("Changed ful.y to %d\n", ful.y());
      }
      if ((((RLEPair *) intrvl->item)->row > flr.y()) || (flr.y()==-1)) {
	flr.y() = ((RLEPair *) intrvl->item)->row;
//	printf("Changed flr.y to %d\n", flr.y());
      }      
      list->first = intrvl->next;
      if (intrvl->next != NULL)
	intrvl->next->previous = NULL;
      delete ((RLEPair *) (intrvl->item));
      delete intrvl;               //so the letter O won't go forever
      counter++;
      list->length--;
    }

  delete list;
  return counter;

}

void Component::setProperties(BitMap * map)  // was BitMap
/*--------------------------------------------------------------
Primary Purpose: Set the property vector for this component
Arguments:  The BitMap to which this component belongs
Return Value:
Effects: The component is divided into a 5 by 5 grid.  A gray
    scale (0 - 255) for each section is determined.  The gray scale
    is 0 for all white, 255 for all black, but normally will be somewhere
    between the two.  The gray scales are represented in properties
    0-24. 
    Property 25 is the grayscale accross the top.
    Property 26 is the grayscale accross the bottom.
    Property 27 is the width/height ratio again scaled to (0-255)
    Actually the formula for property 27 is
        width/ height * 255  if height > width
        1- height/width * 255 if width > height
    This way near 0 is very tall and thin
             near 128 height near width
	     near 255 very wide
    Property 28 is  Indicator of a vertically disjoint character
     like i and j.
    Also the total number of black pixels is set in fnumBits.
    // This is not used at this time.    
Constraints: The data fields ful and flr must already be set 
   before calling this function. These fields specify a bounding
   box for the character within the BitMap.
Rev: 12/9 KM
---------------------------------------------------------------*/
{
  if (ful > flr) 
    printf("Problem\n");
  assert (ful <= flr);
  short int hflag[NumHorizDiv + 1];  // flags horizontal section dividers
  short int vflag[NumVertDiv + 1];   // flags vertical section dividers
  float height, width;
  int propNum;
  float darkest = 0;
  float lightest; 
  int darkrow = 0;
  int lightrow = 0;

  Point sectionLr, sectionUl;
  // Set Number of bits
  fnumBits = map->pixelsInRegion(ful, flr);

  setSectionFlags(hflag, vflag);
  for (int r = 0; r < NumVertDiv; r++)
    for (int c = 0; c < NumHorizDiv; c++)
      {
	propNum = (r * NumHorizDiv) + c;
	sectionUl = Point(hflag[c], vflag[r]);
	sectionLr = Point(hflag[c+1]-1, vflag[r+1]-1);
	if (sectionUl <= sectionLr)
	  fproperty[propNum] = map->grayScale(sectionUl, sectionLr); 
	assert(fproperty[propNum] >= 0 && fproperty[propNum] < 256);
      }

  // set the height/width ratio
  // 0 is very thin 128 is even 256 is very wide.
  width = flr.x() - ful.x() + 1;
  height = flr.y() - ful.y() + 1;
 
  // Grayscale across the top - Indicator of top bar
  sectionUl = Point(ful.x(), ful.y());
  sectionLr = Point(flr.x(), ful.y() + (int)(height/(NumVertDiv*2)));
  fproperty[25] = map->grayScale(sectionUl, sectionLr);

   // Grayscale across bottom - Indicator of a foot for l opposed to 1
  sectionUl = Point(ful.x(),  flr.y() -  (int)(height/(NumVertDiv*2)));
  sectionLr = Point(flr.x(),  flr.y());
  fproperty[26] = map->grayScale(sectionUl, sectionLr);

  float hdivw = (float)height/width;
  float wdivh = (float) width/height;
  if (width > height)
       fproperty[27]= (short int) ((1- hdivw/2)*255);
  else
    fproperty[27] = (short int)((wdivh/2)* 255);

  // is this a disjoint character like i or j  255 = yes 0 = no
  fproperty[28]=0;
  lightest = width;
  for(int row = ful.y(); row < flr.y(); row++)
      {
	int pixelsThisRow = pixelsBetween(map->row(row), ful.x(), flr.x());
	if(!(pixelsThisRow))
	      fproperty[28]=255;
      }

  fproperty[29]= 0;
    for(int p = 0; p < numProperties; p++)	
      assert(fproperty[p] >= 0 && fproperty[p] < 256);


}

void Component::setSectionFlags(short int hflag[], short int vflag[])
/*--------------------------------------------------------------
Primary Purpose: Breaks this component into a grid NumHorizDiv X NumVertDiv
    for determining grayscale property vectors.
Arguments:  hflag[] is an empty array to be filled by this procedure with
    the starting columns of each horizontal subdivision.  vflag[] will
    be filled with the vertical subdivisions.
Effects:  fills hflag[] with the starting column for each subdivision.
     The last element of the array is actually the pixel immediately 
     following the last  subdivision.  The last subdivision contains any 
     remaining pixels that did not divide evenly amongst the divisions.
     vflag[NumHorizDiv] is comparable for vertical supdivisions.
     Example ful = (0,25) flr = (52,46) NumHorizDiv = NumVertDiv = 5
         hflag[6] = { 0,10,20,30,40,53 }
	 vflag[6] = {25.29.33.37.41.47 } 
Constraints: ful and flr must be set to mark the bounding box before
   calling this procedure.
Rev:  10/27 KM
---------------------------------------------------------------*/
{
  int ulx = ful.x();  int uly = ful.y();
  int lrx = flr.x();  int lry = flr.y();

  int width =  lrx - ulx+1;
  int height = lry - uly+1;

  int horizDiv = width/NumHorizDiv;
  int vertDiv = height/NumVertDiv;

  int horizExtra = width - horizDiv*NumHorizDiv;
  int vertExtra = height - vertDiv*NumVertDiv;

  int i, add;
  for (i = 0; i < NumHorizDiv; i++)
    {
    if(horizExtra - i > 0) add = i; else add = horizExtra;
    hflag[i] = ulx + (i*horizDiv)+ add;
  }
  hflag[i] = lrx + 1;              // Closes off last division

  int j;
  for(j = 0; j < NumVertDiv; j ++)
      {
    if(vertExtra - j > 0) add = j; else add = vertExtra;
    vflag[j] = uly + (j*vertDiv)+ add;
  }
  vflag[j] = lry + 1;

    


}


Distance Component::distance(Component * comp) 
/*--------------------------------------------------------------
Primary Purpose: Determines heuristic distance between two components
Arguments:  Another component to compare
Return Value: integer value which represents the distance between two 
   components. Distance = sum over i of
                       weight *square (this->fproperty[i] - comp->fproperty[i])
		       weight for i == 27, 28 is 3 weight is 1 for all other 
		       properties
Constraints: setProperties must have been run on both components

Rev: 11/1 KM
---------------------------------------------------------------*/
{
  Property * a = fproperty;
  Property * b = comp->properties();
  Distance dist=0;
  int dif=0;
  int worst = 0;
  int weight = 1;

  for(int i= 0; i < numProperties; i++)
    { 


     if (i == 27 || i == 28) weight = 3;
     else weight = 1;
     
       dif = (a[i] - b[i]);      
      dist += weight*dif*dif;
    }

  return dist;
}









void printVector(short int vector[], int size)
{
  for (int i = 0; i < size; i++)
    cout << vector[i] << " " ;
  cout << endl;

}


void testProperties(Component* c, BitMap * map)
{
  short int hflag[NumHorizDiv + 1];  // flags horizontal section dividers
  short int vflag[NumVertDiv + 1];   // flags vertical section dividers

  cout << "First test subDivisions  " << endl;
  c->setSectionFlags(hflag, vflag);
  cout << "Horizontal flags" <<endl;
  printVector(hflag, NumHorizDiv + 1);
  cout << "Vertical flags" <<endl;
  printVector(vflag, NumHorizDiv + 1);

  cout << "Now lets look at the properties " << endl;
  // setSectionFlags will actually get called again within setProperties
  c->setProperties(map);
  printVector(c->properties(), NumHorizDiv*NumVertDiv + 1);
  cout << endl << " The distance of this component from itself: " << " ";
  cout << c->distance(c) << endl;

}

void Component::display_bounding_box()
{
  int ulx = (ul()).x();
  int uly = (ul()).y();
  int lrx = (lr()).x();
  int lry = (lr()).y();
  scale(ulx); scale(uly); scale(lrx); scale(lry);
  
  docommand(".main_window.display.work_space create rectangle %d %d %d %d -outline blue -tags IMAGE_TAG",  ulx, uly, lrx, lry);
}  

Distance Component::recognize(Component * learnedchars)
// This is out of date. Current recognize is below
{
  Distance d, nextd;
  char id;

//  printf("Another call to recognize\n");
  d = (256*256)*numProperties;  // this is the biggest distance
                        

	       
    for (int i = 0; i < 256; i++)
	{
	  if(learnedchars[i].confid() != 0)
	      {
		nextd = distance(&learnedchars[i]);
//		printf("Distance = %d, character = %c\n", nextd, i);
		if (nextd < d)
		    {
		      d = nextd;
		      id = (char) i;
		    }
	      }
	  
	}
  
  fasciiId = id;
  /*  printf("Recognized a Component: %c\n", id); */
  return d;
}


Distance Component::recognize(Components * learnedgroups, bool allGroups)
{
  Distance d, worstDistance,nextd;
  char id;
  short int fontid;
  float tempd;
  worstDistance = 150000;

//  printf("Another call to new recognize\n");
  d = (65536)*numProperties;  // this is the biggest distance
  
                        
    fconfid = 0;

    for(int g = 0; g < NumCharGroups &&
 	((fconfid < ConfidenceThreshold) || allGroups); g++)
    {
      int offset = (charGroup+g) % NumCharGroups;
      if (offset == 4 && charGroup != 4) continue;
      for (ListElement* ptr = learnedgroups[offset].first; ptr != NULL;
	 ptr = ptr->next)
	{
	  Component * item = (Component *) ptr->item;
	  nextd = distance(item);
//	  printf("Distance = %d, character = %c\n", nextd, i);
	  if (nextd < d)
	      {
		d = nextd;
		id = item->fasciiId;
		fontid = item->ffontId;
	      }

	}

      if (d >= worstDistance) 
	  tempd = worstDistance - 1;
      else tempd = d;
	
	fconfid = (unsigned short int)
	  (255 - (tempd/worstDistance)*256);
      if(charGroup == 4) break;  // dont check other groups for floaters
    }
  

  fasciiId = id;


  ffontId = fontid;
//  printf("Recognized a Component: %c in font#%d  %d %u\n", 
//	 id, fontid, fconfid, d);



  return d;
}



int Component::vertShrink(BitMap * bitmap)
{
  int r;
  int shrunk = 0;
	for(r = ful.y(); r < flr.y(); r++)
	      if (pixelsBetween(bitmap->row(r), ful.x(), flr.x()))
		  {
		    ful.y() = r;
		    shrunk = 1;
		    break;
		  }
	for(r = flr.y(); r > ful.y(); r--)
	      if (pixelsBetween(bitmap->row(r), ful.x(), flr.x()))
		  {
		    flr.y() = r;
		    shrunk = 1;
		    break;		    
		  }
		
  return shrunk;
      }