reference/ocr-simple/RLEMap.cc
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 "RLEMap.h"
#include "RLEPair.h"
#include "tcl_interface.h"
#include "status_message.h"

 /* *****************************************************************
  * RLEMap.cc - Member functions for an RLEMap                      *
  *         
  *	RLEMap() - Constructor
  *     ~RLEMap() - Destructor
  *
  *	int imageLength();
  *	int imageWidth();
  *	MapStatus & status;
  *  
  * Below is an index of the other functions and the files where they 
  * appear.  
  *
  *   	MapStatus readMap(char * filename) - RLEMap_readMap.cc
  *	MapStatus WriteMap(char * filename);
  *
  *	// Data Access and low level manipulation functions  
  *     RLEPairs * row(int i) - Returns a pointer to the list of RLEPairs
  *     for row i.                       
  *     MapStatus setBit(Point point, Color clr);
  *	Color readBit(Point point);
  *
  *
   ***************************************************************/

RLEMap::RLEMap()
: fMapData(NULL), fImageLength(0), fImageWidth(0), fStatus(EMPTY)
/*--------------------------------------------------------------
Primary Function: Constructor
Return Value: pointer to new RLEMap
Effects: Initialize status to empty other values to zero
Rev: 10/6/95  KM
---------------------------------------------------------------*/
{ }



 RLEMap::~RLEMap()
/*--------------------------------------------------------------
Primary Purpose: destructor
Effects: Deletes each row of RLEPairs then the array of rows
Rev: 10/6/95   KM
---------------------------------------------------------------*/
{
  if (fMapData != NULL)
    {
      int i;


      // delete each row
      for (i=0; i< fImageLength; i++)
	  {
	    delete fMapData[i];
	  }
      // delete array of rows
        delete fMapData;   
    }
};

int & RLEMap::imageLength()
/*--------------------------------------------------------------
Return Value: vertical length of image in pixels
Constraints: readMap() must have been run and fStatus be VALID
Rev: 10/6 KM
---------------------------------------------------------------*/
  {
  return fImageLength;

};


int & RLEMap::imageWidth()
/*--------------------------------------------------------------
Return Value: horizontal width of image in pixels
Constraints: readMap() must have been run and fStatus be valid
Rev: 10/20 KM
---------------------------------------------------------------*/
{
  return fImageWidth;

}


MapStatus & RLEMap::status()
/*--------------------------------------------------------------
Return Value: return reference to current status EMPTY, VALID etc..
Rev: 10/6/95 KM
---------------------------------------------------------------*/
{
  return fStatus;

}



RLEPairs *  RLEMap::operator [](int i)
/*--------------------------------------------------------------
Arguments: i is the row # of the RLEPair list to be returned
Return Value: A pointer to the list of RLEPairs in row i
Rev:  10/20/95 KM
---------------------------------------------------------------*/
{

  return fMapData[i];
}


RLEPairs * RLEMap::row(int i)
// Same as overloaded [] function above
{
  return fMapData[i];
}



MapStatus RLEMap::readMap(char * filename)
/*--------------------------------------------------------------
Primary Purpose: Read an RLEMap from a TIFF file
Arguments: filename of TIFF file
Return Value: A MapStatus, either VALID or READERROR
Effects:
  *  RLEMap::readMap(filename) will read a two level TIFF file
  *  and place it in an RLEMap.  The private fields of the RLEMap
  *  set are:
         fImageWidth - the pixel width of the image
	 fImageLength - the vertical pixel length of the image
	 fstat - the status of the image VALID or READERROR
         fMapData - an array of pointers to lists of RLEPairs
Constraints: filename must be a two level TIFF file
Rev: 10/20/95  Portions Borrowed from Assignment 1
---------------------------------------------------------------*/
{
  TIFF *tif;
  unsigned char * buf;
  short photometric;

  // Open File - Read length and width

  tif = TIFFOpen (filename, "r");
  if(tif == NULL)
    return READERROR;

  TIFFGetField (tif, TIFFTAG_IMAGELENGTH, &fImageLength);
  TIFFGetField (tif, TIFFTAG_IMAGEWIDTH, &fImageWidth);
  TIFFGetField (tif, TIFFTAG_PHOTOMETRIC, &photometric);
  
  printf("open succeeded on file %s.  length = %d. width = %d ",
	 filename, fImageLength, fImageWidth);
  if(photometric == PHOTOMETRIC_MINISWHITE)
    printf("min-is-white format\n");
  else if(photometric == PHOTOMETRIC_MINISBLACK )
    printf("min-is-black format\n");
  else
    printf("with an unknown photometric: %d\n", photometric);

  // allocate buffer and array for data
  int numCharsInBuf = fImageWidth / 8  +1 ;
  buf = new unsigned char[numCharsInBuf];
  fMapData = new (RLEPairs*)[fImageLength]; 
 
 for (int row = 0; row < fImageLength; ++row)
    {
      TIFFReadScanline(tif,buf,row,0);
      if(photometric != PHOTOMETRIC_MINISWHITE)  /* invert anything except white */
	invertBitsInBuffer(buf, numCharsInBuf);

      // Create a list of RLEPairs for this row and fill with buffer data
      fMapData[row] = new RLEPairs(row);  
      fMapData[row]->fill(buf, numCharsInBuf, row);
    }
  TIFFClose(tif);
   
  return VALID;
}

short int RLEMap::grayScale(Point ul, Point lr)
// Dummy function for now
{
   int numPixels = pixelsInRegion( ul, lr);
   int area = (lr.x() - ul.x()+1) * (lr.y() - ul.y()+1);
   if (area < numPixels) {
     printf("Uh oh! Area = %d and pixels = %d\n", area, numPixels);
     assert(area >= numPixels);
   }
   short int gscale =(short int)(((float)numPixels/area) * 255);
   
   return gscale;
 }

int RLEMap::pixelsInRegion(Point ul, Point lr)
{
  assert (ul >= Point(0,0));
  assert (ul <= lr);
  assert (lr <= Point(fImageWidth, fImageLength));

  int ulx = ul.x(); int uly = ul.y();
  int lrx = lr.x(); int lry = lr.y();
  int numPixels = 0;
  RLEPairs * curRow;

  for (int r = uly; r <= lry; r++)
    {
      curRow = row(r);
      numPixels += curRow->pixelsBetween(ulx, lrx);
//      cout << curRow->pixelsBetween(ulx,lrx) <<" ";
 //     cout << numPixels << endl;
    }
  
  return numPixels;

}

ListElement*
RLEMap::FindNearVertDot(int startCol, int endCol, int startRow, int endRow) 
/*--------------------------------------------------------------
Primary Purpose: Return closest interval to startRow within bounds of
                 startCol and endRow in the direction of endRow. Finds
                 closest dot vertically from startRow.
Arguments: startRow is row to start from, startCol and endCol are
           left and right boundaries of search. Search in the direction
	   of endRow.
Return Value: An RLE interval - pointer to a list element in RLEPairs 
Effects:
Constraints: startRow < endRow
---------------------------------------------------------------*/
{
  ListElement* current;
  if (startRow < endRow) {
    for (int i = startRow+2; i <= endRow; i++) {
      current = fMapData[i]->first;
      while (current != NULL) {
	if ((((RLEPair *) current->item)->start <= endCol)
	    && (((RLEPair *) current->item)->end >= startCol))
	  return current;
	current = current->next;
      }
    }
  } else {
    for (int i = startRow-2; i >= endRow; i--) {
      current = fMapData[i]->first;
      while (current != NULL) {
	if ((((RLEPair *) current->item)->start <= endCol)
	    && (((RLEPair *) current->item)->end >= startCol))
	  return current;
	current = current->next;
      }
    }
  }
  return NULL;
}





ListElement*
RLEMap::FindNearHorizDot(int startCol, int startRow, int endRow) 
/*--------------------------------------------------------------
Primary Purpose: Return closest interval to startCol within bounds of
                 startRow and endRow (startRow is lower). Finds
                 closest dot horizontally from startCol.
Arguments: startCol is column to start from, startRow and endRow are
           upper and lower boundaries of search
Return Value: An RLE interval - pointer to a list element in RLEPairs 
Effects:
Constraints: startRow < endRow
---------------------------------------------------------------*/
{
  ListElement* answer = NULL;
  ListElement* current;
  int closest = fImageWidth;

  for (int i = startRow; i <= endRow; i++) {
    current = fMapData[i]->first;
    while ((current != NULL) && (((RLEPair *) current->item)->end 
				 < startCol)) {
      current = current->next;
    }
    if ((current != NULL) && (((RLEPair *) current->item)->start < closest)) {
      answer = current;
      closest = ((RLEPair *) answer->item)->start;
    }
  }
  return answer;
}




void testRLEMap(char * filename)
/*--------------------------------------------------------------
Primary Purpose: Test the reading of tiff files into RLE format
Effects:  Reads filename,  puts it into RLE format then prints
Rev:  10/7/95 KM
---------------------------------------------------------------*/
{
  RLEMap m;

  m.readMap(filename);
  if (m.imageLength() < 100)  printMap(&m);
  testpixelsBetween(&m);   // In RLEPairs.cc - tests pixelsBetween function
}


void printMap(RLEMap * map)
{
  int startX = 0;
  int endX = 0;
  int pos;    
  RLEPair * item;
  RLEPairs * rowdata;

  RLEMap & m = *map;

  for (int r = 0; r < m.imageLength(); r++)
    {
      startX = 0;
      endX = -1;
      rowdata = m[r];

      for (ListElement* ptr = rowdata->first; ptr != NULL; ptr = ptr->next) 
	{
	  item = (RLEPair *)(ptr->item);
	  startX = item->start;	  
	  for ( pos = endX+1; pos< startX; pos++)
	  cout << " ";
          endX = item->end;	  
	  for ( pos = startX; pos <= endX; pos++)
	    cout << "X";
	}
      cout << "" << endl;
    }

}

void RLEMap::printPairs(int startRow, int endRow)
/*--------------------------------------------------------------
Primary Purpose: Prints RLE Pairs for this map from startRow to endRow  
Rev:11/2 KM
---------------------------------------------------------------*/
{
  int startX, endX;
  RLEPair * item;
  RLEPairs * rowdata;

  RLEMap & m = *this;
  cout << "printing rows " << startRow << " to " << endRow << endl;
  for (int r = startRow; r <= endRow; r++)
    {
      rowdata = m[r];

      cout << "row " << r << " ";

      for (ListElement *ptr = rowdata->first; ptr != NULL; ptr = ptr->next) 
	{
	  item = (RLEPair *)(ptr->item);
	  startX = item->start;	  
	  endX = item->end;
	  cout << "(" << startX << "," << endX <<")";
	}
      cout << endl;
    }
}

void testpixelsBetween(RLEMap * map)
// tests out a row by making sure that pixels between 
// 0 and ImageWidth - 1 == pixels in sub ranges of 29 pixels
// Test performed on center row.
{
  int start = 0;
  int end = 28;

  int pcount;
  int sum = 0;
  RLEPairs * pairs;
  int row;

  for (row = 0; row < map->imageLength(); row++)
    {
      pairs = (*map)[row];  
      while (start <= map->imageWidth())
	{
	  pcount = pairs->pixelsBetween(start, end);
//	  printf("row %d col %d to %d - %d pixels\n",row, start,end,pcount);
	  sum += pcount;
	  start +=29;
	  end +=29;
	}
      if (sum !=0)
	printf("row %d sum was %d , should be %d\n", row, sum, pairs->numPixels);
      assert(sum == pairs->numPixels);
      start = 0;
      end = 28;
      sum = 0;
    }

  delete pairs;
}

int RLEMap::deskew()
/* going to be a (near-blind) steal from fateman */
/*--------------------------------------------------------------
Primary Purpose: deskewing an RLEMap
Arguments: none 
Return Value: 1 if the page is altered, 0 if not
Effects: RLEMap is straightened out
Constraints: RLE shouldn't be tilted too much (< 10deg)
Rev: AR 11/1/95
---------------------------------------------------------------*/
{
  double skew = -get_skew(this);  /* skew in rad */
  if((skew >= MINIMUM_SKEW_ANGLE)||(skew <= - MINIMUM_SKEW_ANGLE))
    {
      double h = tan(skew / (180 / M_PI));  
      if(h > 0)
	  {
	    tilt_and_slant(1/h, 1); /* clockwise */
	    return 1;
	  }
      else if (h < 0)
	  {
	    tilt_and_slant(-(1/h), -1); /* counter clockwise */
	    return 1;
	  }
      else
	return 0;
    }
  else 
    return 0;
}

#define DEBUG_TILT_AND_SLANT 1
void RLEMap::tilt_and_slant(double step, int direction)
/*--------------------------------------------------------------
Primary Purpose: do the work of shifting the RLEMap
Arguments: step--something about how many rows to go before shifting,
           direction--counterclockwise or clockwise
Return Value: none
Effects: rotates the RLEMap some ammount by tilting the map slightly,
        then slanting it. (duh). Not an exact rotation
Constraints:
Rev: AR 11/1/95
---------------------------------------------------------------*/
{
  if(DEBUG_TILT_AND_SLANT)
    printf("Call to tilt_and_slant: step = %lf, direction = %d\n ", step, direction);
  if(direction > 0)
    {
      tilt(step, direction);  
      slant(step, direction);
    }
  else
    {
      slant(step, -direction);
      tilt(step, direction); 
    }
}     

#define DEBUG_SLANT 1
/*  "slant a picture by shifting lines horizontally 1 bit every step rows"
  ;; dir 1 means shift to right as row number increases
  ;; dir -1 means shift by left
  ;; this does not rotate the picture, since rows are each unchanged.
  ;; the effect of a positive direction, say (slantpic pic 3 3 1)
  ;; is to "italicize".
*/  
void RLEMap::slant(double step, int direction)
{
  if(DEBUG_SLANT)
    printf("Slant called, step = %lf, dir = %d\n", step, direction);
  fImageWidth += (int)((double)fImageLength / (double)step);
  int shift_amount = direction;
  int num_steps = 1;
  for(int i = 0; i < fImageLength; i++)
      {
	if(i > (num_steps*(int)step))  
/* if we have gone through step rows, increment the shift */
	    {
	      shift_amount += direction;
	      num_steps++;
	    }
/*	printf("Shifting row %d by %d\n", i, shift_amount);  */
	fMapData[i]->shift(shift_amount);
      }
}



void RLEMap::display_intervals(char* color)
{
  if(!DISPLAY_IMAGE)
    return;
  double skip;
  last_status = 0.0;
  printf("SCALE_FACTOR = %lf  ", SCALE_FACTOR);
  skip = 1.0 / SCALE_FACTOR;
  printf("Skip = %lf\n", skip);

  /* delete any garbage hanging around */
  docommand(".main_window.display.work_space delete all");

  set_status("Displaying Image: 0%...");
  for(int i= 0; (int)(i*skip) < ((double)fImageLength); i++)
    {
      set_display_status((int)(i*skip), fImageLength);
      fMapData[(int)(i*skip)]->draw_pairs(i, color, 1.0/skip);
    }
  last_status = 0.0;
  update();
  set_status("Displaying Image: Done");
}

void RLEMap::tilt(double step, int direction)
{
/*    printf("tilt called, step = %lf, dir = %d\n", step, direction); */
  int old_height = fImageLength;
  int new_height = /* ceiling */ (int)(((double)fImageWidth) / step) + old_height;
  int delta = old_height - new_height;
  RLEPairs ** new_data = new RLEPairs*[new_height];
  for(int i = 0; i < new_height; i++)
      {
	new_data[i] = new RLEPairs(i);
      }
  for(int j = 0; j < old_height; j++)
      {
	tilt_row(j, delta, new_data, step, direction);
      }
  fMapData = new_data;  /* probably want to delete old data */
  fImageLength = new_height;
  display_intervals("black");
}  


void RLEMap::tilt_row(int old_row_index, int old_new_row_diff, RLEPairs** new_data, double step, int direction)
{
/*    printf("Tilt row called: old row = %d, row diff = %d, step = %lf, dir = %d\n", old_row_index, old_new_row_diff, step, direction); */

  double cur_x = 0;  /* I don't know what will happen with negative rows */
  double new_x;
  int cur_y = old_row_index + (old_new_row_diff * direction);
  
  while(((new_x = cur_x + step) < fImageWidth) &&
	(cur_y >= 0) && (cur_y < fImageLength))
    {
      RLEPairs* new_pairs;
      new_pairs = (fMapData[old_row_index])->extract((int) cur_x, (int)new_x);
      new_data[cur_y]->merge(new_pairs);
      cur_x = new_x + 1;
      cur_y += direction;
    }
}