TBOX PIXROW::bounding_box() const {
  inT16 i;
  inT16 y_coord;
  inT16 min_x = MAX_INT16 - 1;
  inT16 min_y = MAX_INT16 - 1;
  inT16 max_x = -MAX_INT16 + 1;
  inT16 max_y = -MAX_INT16 + 1;

  for (i = 0; i < row_count; i++) {
    y_coord = row_offset + i;
    if (min[i] <= max[i]) {
      if (y_coord < min_y)
        min_y = y_coord;
      if (y_coord + 1 > max_y)
        max_y = y_coord + 1;
      if (min[i] < min_x)
        min_x = min[i];
      if (max[i] + 1 > max_x)
        max_x = max[i] + 1;
    }
  }
  if (min_x > max_x || min_y > max_y)
    return TBOX ();
  else
    return TBOX (ICOORD (min_x, min_y), ICOORD (max_x, max_y));
}
Example #2
0
void BLOBNBOX::chop(                        //chop blobs
                    BLOBNBOX_IT *start_it,  //location of this
                    BLOBNBOX_IT *end_it,    //iterator
                    FCOORD rotation,        //for landscape
                    float xheight           //of line
                   ) {
  inT16 blobcount;               //no of blobs
  BLOBNBOX *newblob;             //fake blob
  BLOBNBOX *blob;                //current blob
  inT16 blobindex;               //number of chop
  inT16 leftx;                   //left edge of blob
  float blobwidth;               //width of each
  float rightx;                  //right edge to scan
  float ymin, ymax;              //limits of new blob
  float test_ymin, test_ymax;    //limits of part blob
  ICOORD bl, tr;                 //corners of box
  BLOBNBOX_IT blob_it;           //blob iterator

                                 //get no of chops
  blobcount = (inT16) floor (box.width () / xheight);
  if (blobcount > 1 && cblob_ptr != NULL) {
                                 //width of each
    blobwidth = (float) (box.width () + 1) / blobcount;
    for (blobindex = blobcount - 1, rightx = box.right ();
    blobindex >= 0; blobindex--, rightx -= blobwidth) {
      ymin = (float) MAX_INT32;
      ymax = (float) -MAX_INT32;
      blob_it = *start_it;
      do {
        blob = blob_it.data ();
        find_cblob_vlimits(blob->cblob_ptr, rightx - blobwidth,
                           rightx,
            /*rotation, */ test_ymin, test_ymax);
        blob_it.forward ();
        UpdateRange(test_ymin, test_ymax, &ymin, &ymax);
      }
      while (blob != end_it->data ());
      if (ymin < ymax) {
        leftx = (inT16) floor (rightx - blobwidth);
        if (leftx < box.left ())
          leftx = box.left ();   //clip to real box
        bl = ICOORD (leftx, (inT16) floor (ymin));
        tr = ICOORD ((inT16) ceil (rightx), (inT16) ceil (ymax));
        if (blobindex == 0)
          box = TBOX (bl, tr);    //change box
        else {
          newblob = new BLOBNBOX;
                                 //box is all it has
          newblob->box = TBOX (bl, tr);
                                 //stay on current
          newblob->base_char_top_ = tr.y();
          newblob->base_char_bottom_ = bl.y();
          end_it->add_after_stay_put (newblob);
        }
      }
    }
  }
}
Example #3
0
// Recognizes a word or group of words, converting to WERD_RES in *words.
// Analogous to classify_word_pass1, but can handle a group of words as well.
void Tesseract::LSTMRecognizeWord(const BLOCK& block, ROW *row, WERD_RES *word,
                                  PointerVector<WERD_RES>* words) {
  TBOX word_box = word->word->bounding_box();
  // Get the word image - no frills.
  if (tessedit_pageseg_mode == PSM_SINGLE_WORD ||
      tessedit_pageseg_mode == PSM_RAW_LINE) {
    // In single word mode, use the whole image without any other row/word
    // interpretation.
    word_box = TBOX(0, 0, ImageWidth(), ImageHeight());
  } else {
    float baseline = row->base_line((word_box.left() + word_box.right()) / 2);
    if (baseline + row->descenders() < word_box.bottom())
      word_box.set_bottom(baseline + row->descenders());
    if (baseline + row->x_height() + row->ascenders() > word_box.top())
      word_box.set_top(baseline + row->x_height() + row->ascenders());
  }
  ImageData* im_data = GetRectImage(word_box, block, kImagePadding, &word_box);
  if (im_data == NULL) return;
  lstm_recognizer_->RecognizeLine(*im_data, true, classify_debug_level > 0,
                                  kWorstDictCertainty / kCertaintyScale,
                                  lstm_use_matrix, &unicharset, word_box, 2.0,
                                  false, words);
  delete im_data;
  SearchWords(words);
}
Example #4
0
void POLY_BLOCK::compute_bb() {  //constructor
  ICOORD ibl, itr;               //integer bb
  ICOORD botleft;                //bounding box
  ICOORD topright;
  ICOORD pos;                    //current pos;
  ICOORDELT_IT pts = &vertices;  //iterator

  botleft = *pts.data ();
  topright = botleft;
  do {
    pos = *pts.data ();
    if (pos.x () < botleft.x ())
                                 //get bounding box
      botleft = ICOORD (pos.x (), botleft.y ());
    if (pos.y () < botleft.y ())
      botleft = ICOORD (botleft.x (), pos.y ());
    if (pos.x () > topright.x ())
      topright = ICOORD (pos.x (), topright.y ());
    if (pos.y () > topright.y ())
      topright = ICOORD (topright.x (), pos.y ());
    pts.forward ();
  }
  while (!pts.at_first ());
  ibl = ICOORD (botleft.x (), botleft.y ());
  itr = ICOORD (topright.x (), topright.y ());
  box = TBOX (ibl, itr);
}
Example #5
0
// Generates a TrainingSample from a TBLOB. Extracts features and sets
// the bounding box, so classifiers that operate on the image can work.
// TODO(rays) Make BlobToTrainingSample a member of Classify now that
// the FlexFx and FeatureDescription code have been removed and LearnBlob
// is now a member of Classify.
TrainingSample* BlobToTrainingSample(
    const TBLOB& blob, bool nonlinear_norm, INT_FX_RESULT_STRUCT* fx_info,
    GenericVector<INT_FEATURE_STRUCT>* bl_features) {
  GenericVector<INT_FEATURE_STRUCT> cn_features;
  Classify::ExtractFeatures(blob, nonlinear_norm, bl_features,
                            &cn_features, fx_info, nullptr);
  // TODO(rays) Use blob->PreciseBoundingBox() instead.
  TBOX box = blob.bounding_box();
  TrainingSample* sample = nullptr;
  int num_features = fx_info->NumCN;
  if (num_features > 0) {
    sample = TrainingSample::CopyFromFeatures(*fx_info, box, &cn_features[0],
                                              num_features);
  }
  if (sample != nullptr) {
    // Set the bounding box (in original image coordinates) in the sample.
    TPOINT topleft, botright;
    topleft.x = box.left();
    topleft.y = box.top();
    botright.x = box.right();
    botright.y = box.bottom();
    TPOINT original_topleft, original_botright;
    blob.denorm().DenormTransform(nullptr, topleft, &original_topleft);
    blob.denorm().DenormTransform(nullptr, botright, &original_botright);
    sample->set_bounding_box(TBOX(original_topleft.x, original_botright.y,
                                  original_botright.x, original_topleft.y));
  }
  return sample;
}
void OUTLINE::compute_bb() {  //constructor
  ICOORD ibl, itr;               //integer bb
  FCOORD botleft;                //bounding box
  FCOORD topright;
  FCOORD pos;                    //current pos;
  POLYPT_IT polypts = &outline;  //iterator

  botleft = polypts.data ()->pos;
  topright = botleft;
  start = ICOORD ((inT16) botleft.x (), (inT16) botleft.y ());
  do {
    pos = polypts.data ()->pos;
    if (pos.x () < botleft.x ())
                                 //get bounding box
      botleft = FCOORD (pos.x (), botleft.y ());
    if (pos.y () < botleft.y ())
      botleft = FCOORD (botleft.x (), pos.y ());
    if (pos.x () > topright.x ())
      topright = FCOORD (pos.x (), topright.y ());
    if (pos.y () > topright.y ())
      topright = FCOORD (topright.x (), pos.y ());
    polypts.forward ();
  }
  while (!polypts.at_first ());
  ibl = ICOORD ((inT16) botleft.x (), (inT16) botleft.y ());
  itr = ICOORD ((inT16) topright.x () + 1, (inT16) topright.y () + 1);
  box = TBOX (ibl, itr);
}
Example #7
0
TBOX TBOX::intersection(  //shared area box
                      const TBOX &box) const {
  inT16 left;
  inT16 bottom;
  inT16 right;
  inT16 top;
  if (overlap (box)) {
    if (box.bot_left.x () > bot_left.x ())
      left = box.bot_left.x ();
    else
      left = bot_left.x ();

    if (box.top_right.x () < top_right.x ())
      right = box.top_right.x ();
    else
      right = top_right.x ();

    if (box.bot_left.y () > bot_left.y ())
      bottom = box.bot_left.y ();
    else
      bottom = bot_left.y ();

    if (box.top_right.y () < top_right.y ())
      top = box.top_right.y ();
    else
      top = top_right.y ();
  }
  else {
    left = MAX_INT16;
    bottom = MAX_INT16;
    top = -MAX_INT16;
    right = -MAX_INT16;
  }
  return TBOX (left, bottom, right, top);
}
Example #8
0
TBOX TBOX::bounding_union(  //box enclosing both
                        const TBOX &box) const {
  ICOORD bl;                     //bottom left
  ICOORD tr;                     //top right

  if (box.bot_left.x () < bot_left.x ())
    bl.set_x (box.bot_left.x ());
  else
    bl.set_x (bot_left.x ());

  if (box.top_right.x () > top_right.x ())
    tr.set_x (box.top_right.x ());
  else
    tr.set_x (top_right.x ());

  if (box.bot_left.y () < bot_left.y ())
    bl.set_y (box.bot_left.y ());
  else
    bl.set_y (bot_left.y ());

  if (box.top_right.y () > top_right.y ())
    tr.set_y (box.top_right.y ());
  else
    tr.set_y (top_right.y ());
  return TBOX (bl, tr);
}
Example #9
0
void BLOCK::compress() {  // squash it up
  #define           ROW_SPACING 5

  ROW_IT row_it(&rows);
  ROW *row;
  ICOORD row_spacing (0, ROW_SPACING);

  ICOORDELT_IT icoordelt_it;

  sort_rows();

  box = TBOX (box.topleft (), box.topleft ());
  box.move_bottom_edge (ROW_SPACING);
  for (row_it.mark_cycle_pt (); !row_it.cycled_list (); row_it.forward ()) {
    row = row_it.data ();
    row->move (box.botleft () - row_spacing -
      row->bounding_box ().topleft ());
    box += row->bounding_box ();
  }

  leftside.clear ();
  icoordelt_it.set_to_list (&leftside);
  icoordelt_it.add_to_end (new ICOORDELT (box.left (), box.bottom ()));
  icoordelt_it.add_to_end (new ICOORDELT (box.left (), box.top ()));
  rightside.clear ();
  icoordelt_it.set_to_list (&rightside);
  icoordelt_it.add_to_end (new ICOORDELT (box.right (), box.bottom ()));
  icoordelt_it.add_to_end (new ICOORDELT (box.right (), box.top ()));
}
Example #10
0
TBOX TBOX::intersection(  //shared area box
                      const TBOX &box) const {
  ICOORD bl;                     //bottom left
  ICOORD tr;                     //top right

  if (overlap (box)) {
    if (box.bot_left.x () > bot_left.x ())
      bl.set_x (box.bot_left.x ());
    else
      bl.set_x (bot_left.x ());

    if (box.top_right.x () < top_right.x ())
      tr.set_x (box.top_right.x ());
    else
      tr.set_x (top_right.x ());

    if (box.bot_left.y () > bot_left.y ())
      bl.set_y (box.bot_left.y ());
    else
      bl.set_y (bot_left.y ());

    if (box.top_right.y () < top_right.y ())
      tr.set_y (box.top_right.y ());
    else
      tr.set_y (top_right.y ());
  }
  else {
    bl.set_x (MAX_INT16);
    bl.set_y (MAX_INT16);
    tr.set_x (-MAX_INT16);
    tr.set_y (-MAX_INT16);
  }
  return TBOX (bl, tr);
}
Example #11
0
// Applies the box file based on the image name fname, and resegments
// the words in the block_list (page), with:
// blob-mode: one blob per line in the box file, words as input.
// word/line-mode: one blob per space-delimited unit after the #, and one word
// per line in the box file. (See comment above for box file format.)
// If find_segmentation is true, (word/line mode) then the classifier is used
// to re-segment words/lines to match the space-delimited truth string for
// each box. In this case, the input box may be for a word or even a whole
// text line, and the output words will contain multiple blobs corresponding
// to the space-delimited input string.
// With find_segmentation false, no classifier is needed, but the chopper
// can still be used to correctly segment touching characters with the help
// of the input boxes.
// In the returned PAGE_RES, the WERD_RES are setup as they would be returned
// from normal classification, ie. with a word, chopped_word, rebuild_word,
// seam_array, denorm, box_word, and best_state, but NO best_choice or
// raw_choice, as they would require a UNICHARSET, which we aim to avoid.
// Instead, the correct_text member of WERD_RES is set, and this may be later
// converted to a best_choice using CorrectClassifyWords. CorrectClassifyWords
// is not required before calling ApplyBoxTraining.
PAGE_RES* Tesseract::ApplyBoxes(const STRING& fname,
                                bool find_segmentation,
                                BLOCK_LIST *block_list) {
  GenericVector<TBOX> boxes;
  GenericVector<STRING> texts, full_texts;
  if (!ReadAllBoxes(applybox_page, true, fname, &boxes, &texts, &full_texts,
                    NULL)) {
    return NULL;  // Can't do it.
  }

  int box_count = boxes.size();
  int box_failures = 0;
  // Add an empty everything to the end.
  boxes.push_back(TBOX());
  texts.push_back(STRING());
  full_texts.push_back(STRING());

  // In word mode, we use the boxes to make a word for each box, but
  // in blob mode we use the existing words and maximally chop them first.
  PAGE_RES* page_res = find_segmentation ?
      NULL : SetupApplyBoxes(boxes, block_list);
  clear_any_old_text(block_list);

  for (int i = 0; i < boxes.size() - 1; i++) {
    bool foundit = false;
    if (page_res != NULL) {
      if (i == 0) {
        foundit = ResegmentCharBox(page_res, NULL, boxes[i], boxes[i + 1],
                                   full_texts[i].string());
      } else {
        foundit = ResegmentCharBox(page_res, &boxes[i-1], boxes[i],
                                   boxes[i + 1], full_texts[i].string());
      }
    } else {
      foundit = ResegmentWordBox(block_list, boxes[i], boxes[i + 1],
                                 texts[i].string());
    }
    if (!foundit) {
      box_failures++;
      ReportFailedBox(i, boxes[i], texts[i].string(),
                      "FAILURE! Couldn't find a matching blob");
    }
  }

  if (page_res == NULL) {
    // In word/line mode, we now maximally chop all the words and resegment
    // them with the classifier.
    page_res = SetupApplyBoxes(boxes, block_list);
    ReSegmentByClassification(page_res);
  }
  if (applybox_debug > 0) {
    tprintf("APPLY_BOXES:\n");
    tprintf("   Boxes read from boxfile:  %6d\n", box_count);
    if (box_failures > 0)
      tprintf("   Boxes failed resegmentation:  %6d\n", box_failures);
  }
  TidyUp(page_res);
  return page_res;
}
Example #12
0
TBOX M_Utils::imToCBlobGridCoords(BOX* box, PIX* im) {
  inT16 height = (inT16)im->h;
  inT16 left = (inT16)(box->x);
  inT16 top = height - (inT16)(box->y);
  inT16 right = left + (inT16)(box->w);
  inT16 bottom = height - (box->y+(inT16)(box->h));
  return TBOX(left,bottom,right,top);
}
Example #13
0
// Extract the OCR results, costs (penalty points for uncertainty),
// and the bounding boxes of the characters.
static void extract_result(ELIST_ITERATOR *out,
                           PAGE_RES* page_res) {
  PAGE_RES_IT page_res_it(page_res);
  int word_count = 0;
  while (page_res_it.word() != NULL) {
    WERD_RES *word = page_res_it.word();
    const char *str = word->best_choice->string().string();
    const char *len = word->best_choice->lengths().string();

    if (word_count)
      add_space(out);
    TBOX bln_rect;
    PBLOB_LIST *blobs = word->outword->blob_list();
    PBLOB_IT it(blobs);
    int n = strlen(len);
    TBOX** boxes_to_fix = new TBOX*[n];
    for (int i = 0; i < n; i++) {
      PBLOB *blob = it.data();
      TBOX current = blob->bounding_box();
      bln_rect = bln_rect.bounding_union(current);
      TESS_CHAR *tc = new TESS_CHAR(rating_to_cost(word->best_choice->rating()),
                                    str, *len);
      tc->box = current;
      boxes_to_fix[i] = &tc->box;

      out->add_after_then_move(tc);
      it.forward();
      str += *len;
      len++;
    }

    // Find the word bbox before normalization.
    // Here we can't use the C_BLOB bboxes directly,
    // since connected letters are not yet cut.
    TBOX real_rect = word->word->bounding_box();

    // Denormalize boxes by transforming the bbox of the whole bln word
    // into the denorm bbox (`real_rect') of the whole word.
    double x_stretch = double(real_rect.width()) / bln_rect.width();
    double y_stretch = double(real_rect.height()) / bln_rect.height();
    for (int j = 0; j < n; j++) {
      TBOX *box = boxes_to_fix[j];
      int x0 = int(real_rect.left() +
                   x_stretch * (box->left() - bln_rect.left()) + 0.5);
      int x1 = int(real_rect.left() +
                   x_stretch * (box->right() - bln_rect.left()) + 0.5);
      int y0 = int(real_rect.bottom() +
                   y_stretch * (box->bottom() - bln_rect.bottom()) + 0.5);
      int y1 = int(real_rect.bottom() +
                   y_stretch * (box->top() - bln_rect.bottom()) + 0.5);
      *box = TBOX(ICOORD(x0, y0), ICOORD(x1, y1));
    }
    delete [] boxes_to_fix;

    page_res_it.forward();
    word_count++;
  }
}
Example #14
0
// Parses the given box file string into a page_number, utf8_str, and
// bounding_box. Returns true on a successful parse.
// The box file is assumed to contain box definitions, one per line, of the
// following format for blob-level boxes:
//   <UTF8 str> <left> <bottom> <right> <top> <page id>
// and for word/line-level boxes:
//   WordStr <left> <bottom> <right> <top> <page id> #<space-delimited word str>
// See applyybox.cpp for more information.
bool ParseBoxFileStr(const char* boxfile_str, int* page_number,
                     STRING* utf8_str, TBOX* bounding_box) {
  *bounding_box = TBOX();       // Initialize it to empty.
  *utf8_str = "";
  char uch[kBoxReadBufSize];
  const char *buffptr = boxfile_str;
  // Read the unichar without messing up on Tibetan.
  // According to issue 253 the utf-8 surrogates 85 and A0 are treated
  // as whitespace by sscanf, so it is more reliable to just find
  // ascii space and tab.
  int uch_len = 0;
  // Skip unicode file designation, if present.
  const unsigned char *ubuf = reinterpret_cast<const unsigned char*>(buffptr);
  if (ubuf[0] == 0xef && ubuf[1] == 0xbb && ubuf[2] == 0xbf)
      buffptr += 3;
  // Allow a single blank as the UTF-8 string. Check for empty string and
  // then blindly eat the first character.
  if (*buffptr == '\0') return false;
  do {
    uch[uch_len++] = *buffptr++;
  } while (*buffptr != '\0' && *buffptr != ' ' && *buffptr != '\t' &&
           uch_len < kBoxReadBufSize - 1);
  uch[uch_len] = '\0';
  if (*buffptr != '\0') ++buffptr;
  int x_min, y_min, x_max, y_max;
  *page_number = 0;
  int count = sscanf(buffptr, "%d %d %d %d %d",
                 &x_min, &y_min, &x_max, &y_max, page_number);
  if (count != 5 && count != 4) {
    tprintf("Bad box coordinates in boxfile string! %s\n", ubuf);
    return false;
  }
  // Test for long space-delimited string label.
  if (strcmp(uch, kMultiBlobLabelCode) == 0 &&
      (buffptr = strchr(buffptr, '#')) != NULL) {
    strncpy(uch, buffptr + 1, kBoxReadBufSize - 1);
    uch[kBoxReadBufSize - 1] = '\0';  // Prevent buffer overrun.
    chomp_string(uch);
    uch_len = strlen(uch);
  }
  // Validate UTF8 by making unichars with it.
  int used = 0;
  while (used < uch_len) {
    UNICHAR ch(uch + used, uch_len - used);
    int new_used = ch.utf8_len();
    if (new_used == 0) {
      tprintf("Bad UTF-8 str %s starts with 0x%02x at col %d\n",
              uch + used, uch[used], used + 1);
      return false;
    }
    used += new_used;
  }
  *utf8_str = uch;
  if (x_min > x_max) Swap(&x_min, &x_max);
  if (y_min > y_max) Swap(&y_min, &y_max);
  bounding_box->set_to_given_coords(x_min, y_min, x_max, y_max);
  return true;  // Successfully read a box.
}
Example #15
0
// Compute the coverage and good column count. Coverage is the amount of the
// width of the page (in pixels) that is covered by ColPartitions, which are
// used to provide candidate column layouts.
// Coverage is split into good and bad. Good coverage is provided by
// ColPartitions of a frequent width (according to the callback function
// provided by TabFinder::WidthCB, which accesses stored statistics on the
// widths of ColParititions) and bad coverage is provided by all other
// ColPartitions, even if they have tab vectors at both sides. Thus:
// |-----------------------------------------------------------------|
// |        Double     width    heading                              |
// |-----------------------------------------------------------------|
// |-------------------------------| |-------------------------------|
// |   Common width ColParition    | |  Common width ColPartition    |
// |-------------------------------| |-------------------------------|
// the layout with two common-width columns has better coverage than the
// double width heading, because the coverage is "good," even though less in
// total coverage than the heading, because the heading coverage is "bad."
void ColPartitionSet::ComputeCoverage() {
  // Count the number of good columns and sum their width.
  ColPartition_IT it(&parts_);
  good_column_count_ = 0;
  good_coverage_ = 0;
  bad_coverage_ = 0;
  bounding_box_ = TBOX();
  for (it.mark_cycle_pt(); !it.cycled_list(); it.forward()) {
    ColPartition* part = it.data();
    AddPartitionCoverageAndBox(*part);
  }
}
Example #16
0
/**********************************************************************
 * C_OUTLINE::C_OUTLINE
 *
 * Constructor to build a C_OUTLINE from a C_OUTLINE_FRAG.
 **********************************************************************/
C_OUTLINE::C_OUTLINE (
//constructor
                                 //steps to copy
ICOORD startpt, DIR128 * new_steps,
inT16 length                     //length of loop
):start (startpt), offsets(NULL) {
  inT8 dirdiff;                  //direction difference
  DIR128 prevdir;                //previous direction
  DIR128 dir;                    //current direction
  DIR128 lastdir;                //dir of last step
  TBOX new_box;                   //easy bounding
  inT16 stepindex;               //index to step
  inT16 srcindex;                //source steps
  ICOORD pos;                    //current position

  pos = startpt;
  stepcount = length;            // No. of steps.
  ASSERT_HOST(length >= 0);
  steps = reinterpret_cast<uinT8*>(alloc_mem(step_mem()));  // Get memory.
  memset(steps, 0, step_mem());

  lastdir = new_steps[length - 1];
  prevdir = lastdir;
  for (stepindex = 0, srcindex = 0; srcindex < length;
  stepindex++, srcindex++) {
    new_box = TBOX (pos, pos);
    box += new_box;
                                 //copy steps
    dir = new_steps[srcindex];
    set_step(stepindex, dir);
    dirdiff = dir - prevdir;
    pos += step (stepindex);
    if ((dirdiff == 64 || dirdiff == -64) && stepindex > 0) {
      stepindex -= 2;            //cancel there-and-back
      prevdir = stepindex >= 0 ? step_dir (stepindex) : lastdir;
    }
    else
      prevdir = dir;
  }
  ASSERT_HOST (pos.x () == startpt.x () && pos.y () == startpt.y ());
  do {
    dirdiff = step_dir (stepindex - 1) - step_dir (0);
    if (dirdiff == 64 || dirdiff == -64) {
      start += step (0);
      stepindex -= 2;            //cancel there-and-back
      for (int i = 0; i < stepindex; ++i)
        set_step(i, step_dir(i + 1));
    }
  }
  while (stepindex > 1 && (dirdiff == 64 || dirdiff == -64));
  stepcount = stepindex;
  ASSERT_HOST (stepcount >= 4);
}
Example #17
0
// Return the bounding boxes of columns at the given y-range
void ColPartitionSet::GetColumnBoxes(int y_bottom, int y_top,
                                     ColSegment_LIST *segments) {
  ColPartition_IT it(&parts_);
  ColSegment_IT col_it(segments);
  col_it.move_to_last();
  for (it.mark_cycle_pt(); !it.cycled_list(); it.forward()) {
    ColPartition* part = it.data();
    ICOORD bot_left(part->LeftAtY(y_top), y_bottom);
    ICOORD top_right(part->RightAtY(y_bottom), y_top);
    ColSegment *col_seg = new ColSegment();
    col_seg->InsertBox(TBOX(bot_left, top_right));
    col_it.add_after_then_move(col_seg);
  }
}
Example #18
0
/**********************************************************************
 * char_box_to_tbox
 *
 * Create a TBOX from a character bounding box. If nonzero, the
 * x_offset accounts for any additional padding of the word box that
 * should be taken into account.
 *
 **********************************************************************/
TBOX char_box_to_tbox(Box* char_box, TBOX word_box, int x_offset) {
  l_int32 left;
  l_int32 top;
  l_int32 width;
  l_int32 height;
  l_int32 right;
  l_int32 bottom;

  boxGetGeometry(char_box, &left, &top, &width, &height);
  left += word_box.left() - x_offset;
  right = left + width;
  top = word_box.bottom() + word_box.height() - top;
  bottom = top - height;
  return TBOX(left, bottom, right, top);
}
Example #19
0
// Parses the given box file string into a page_number, utf8_str, and
// bounding_box. Returns true on a successful parse.
// The box file is assumed to contain box definitions, one per line, of the
// following format for blob-level boxes:
//   <UTF8 str> <left> <bottom> <right> <top> <page id>
// and for word/line-level boxes:
//   WordStr <left> <bottom> <right> <top> <page id> #<space-delimited word str>
// See applyybox.cpp for more information.
bool ParseBoxFileStr(const char* boxfile_str, int* page_number,
                     STRING* utf8_str, TBOX* bounding_box) {
  *bounding_box = TBOX();       // Initialize it to empty.
  *utf8_str = "";
  char uch[kBoxReadBufSize];
  const char *buffptr = boxfile_str;
  // Read the unichar without messing up on Tibetan.
  // According to issue 253 the utf-8 surrogates 85 and A0 are treated
  // as whitespace by sscanf, so it is more reliable to just find
  // ascii space and tab.
  int uch_len = 0;
  while (*buffptr != '\0' && *buffptr != ' ' && *buffptr != '\t' &&
         uch_len < kBoxReadBufSize - 1) {
    uch[uch_len++] = *buffptr++;
  }
  uch[uch_len] = '\0';
  if (*buffptr != '\0') ++buffptr;
  int x_min, y_min, x_max, y_max;
  *page_number = 0;
  int count = sscanf(buffptr, "%d %d %d %d %d",
                 &x_min, &y_min, &x_max, &y_max, page_number);
  if (count != 5 && count != 4) {
    tprintf("Bad box coordinates in boxfile string!\n");
    return false;
  }
  // Test for long space-delimited string label.
  if (strcmp(uch, kMultiBlobLabelCode) == 0 &&
      (buffptr = strchr(buffptr, '#')) != NULL) {
    strncpy(uch, buffptr + 1, kBoxReadBufSize);
    chomp_string(uch);
    uch_len = strlen(uch);
  }
  // Validate UTF8 by making unichars with it.
  int used = 0;
  while (used < uch_len) {
    UNICHAR ch(uch + used, uch_len - used);
    int new_used = ch.utf8_len();
    if (new_used == 0) {
      tprintf("Bad UTF-8 str %s starts with 0x%02x at col %d\n",
              uch + used, uch[used], used + 1);
      return false;
    }
    used += new_used;
  }
  *utf8_str = uch;
  bounding_box->set_to_given_coords(x_min, y_min, x_max, y_max);
  return true;  // Successfully read a box.
}
ScrollView* display_clip_image(WERD *word,                //word to be processed
                          IMAGE &bin_image,          //whole image
                          PIXROW_LIST *pixrow_list,  //pixrows built
                          TBOX &pix_box               //box of subimage
                         ) {
  ScrollView* clip_window;            //window for debug
  TBOX word_box = word->bounding_box ();
  int border = word_box.height () / 2;
  TBOX display_box = word_box;

  display_box.move_bottom_edge (-border);
  display_box.move_top_edge (border);
  display_box.move_left_edge (-border);
  display_box.move_right_edge (border);
  display_box -= TBOX (ICOORD (0, 0 - BUG_OFFSET),
    ICOORD (bin_image.get_xsize (),
    bin_image.get_ysize () - BUG_OFFSET));

  pgeditor_msg ("Creating Clip window...");
  clip_window = new ScrollView("Clipped Blobs",
    editor_word_xpos, editor_word_ypos,
    3 * (word_box.width () + 2 * border),
    3 * (word_box.height () + 2 * border),
    display_box.left () + display_box.right (),
    display_box.bottom () - BUG_OFFSET +
    display_box.top () - BUG_OFFSET,
    true);
  // ymin, ymax
  pgeditor_msg ("Creating Clip window...Done");

  clip_window->Clear();
  sv_show_sub_image (&bin_image,
    display_box.left (),
    display_box.bottom (),
    display_box.width (),
    display_box.height (),
    clip_window,
    display_box.left (), display_box.bottom () - BUG_OFFSET);

  word->plot (clip_window, ScrollView::RED);
  word_box.plot (clip_window, ScrollView::BLUE, ScrollView::BLUE);
  pix_box.plot (clip_window, ScrollView::BLUE, ScrollView::BLUE);
  plot_pixrows(pixrow_list, clip_window);
  return clip_window;
}
Example #21
0
SEAM *Wordrec::chop_overlapping_blob(const GenericVector<TBOX>& boxes,
                                     bool italic_blob, WERD_RES *word_res,
                                     int *blob_number) {
  TWERD *word = word_res->chopped_word;
  for (*blob_number = 0; *blob_number < word->NumBlobs(); ++*blob_number) {
    TBLOB *blob = word->blobs[*blob_number];
    TPOINT topleft, botright;
    topleft.x = blob->bounding_box().left();
    topleft.y = blob->bounding_box().top();
    botright.x = blob->bounding_box().right();
    botright.y = blob->bounding_box().bottom();

    TPOINT original_topleft, original_botright;
    word_res->denorm.DenormTransform(NULL, topleft, &original_topleft);
    word_res->denorm.DenormTransform(NULL, botright, &original_botright);

    TBOX original_box = TBOX(original_topleft.x, original_botright.y,
                             original_botright.x, original_topleft.y);

    bool almost_equal_box = false;
    int num_overlap = 0;
    for (int i = 0; i < boxes.size(); i++) {
      if (original_box.overlap_fraction(boxes[i]) > 0.125)
        num_overlap++;
      if (original_box.almost_equal(boxes[i], 3))
        almost_equal_box = true;
    }

    TPOINT location;
    if (divisible_blob(blob, italic_blob, &location) ||
        (!almost_equal_box && num_overlap > 1)) {
      SEAM *seam = attempt_blob_chop(word, blob, *blob_number,
                                     italic_blob, word_res->seam_array);
      if (seam != NULL)
        return seam;
    }
  }

  *blob_number = -1;
  return NULL;
}
Example #22
0
// Returns the bounding box of all the points in the split.
TBOX SPLIT::bounding_box() const {
  return TBOX(
      MIN(point1->pos.x, point2->pos.x), MIN(point1->pos.y, point2->pos.y),
      MAX(point1->pos.x, point2->pos.x), MAX(point1->pos.y, point2->pos.y));
}
Example #23
0
/**
 * process_image_event()
 *
 * User has done something in the image window - mouse down or up.  Work out
 * what it is and do something with it.
 * If DOWN - just remember where it was.
 * If UP - for each word in the selected area do the operation defined by
 * the current mode.
 */
void Tesseract::process_image_event( // action in image win
                                    const SVEvent &event) {
   // The following variable should remain static, since it is used by
   // debug editor, which uses a single Tesseract instance.
  static ICOORD down;
  ICOORD up;
  TBOX selection_box;
  char msg[80];

  switch(event.type) {

    case SVET_SELECTION:
      if (event.type == SVET_SELECTION) {
        down.set_x(event.x + event.x_size);
        down.set_y(event.y + event.y_size);
        if (mode == SHOW_POINT_CMD_EVENT)
          show_point(current_page_res, event.x, event.y);
      }

      up.set_x(event.x);
      up.set_y(event.y);

      selection_box = TBOX(down, up);

      switch(mode) {
        case CHANGE_DISP_CMD_EVENT:
          process_selected_words(
              current_page_res,
              selection_box,
              &tesseract::Tesseract::word_blank_and_set_display);
          break;
       case DUMP_WERD_CMD_EVENT:
          process_selected_words(current_page_res,
                                 selection_box,
                                 &tesseract::Tesseract::word_dumper);
          break;
        case SHOW_BLN_WERD_CMD_EVENT:
          process_selected_words(current_page_res,
                                 selection_box,
                                 &tesseract::Tesseract::word_bln_display);
          break;
        case DEBUG_WERD_CMD_EVENT:
          debug_word(current_page_res, selection_box);
          break;
        case SHOW_POINT_CMD_EVENT:
          break;                 // ignore up event

        case RECOG_WERDS:
          image_win->AddMessage("Recogging selected words");
          this->process_selected_words(current_page_res,
                                       selection_box,
                                       &Tesseract::recog_interactive);
          break;
        case RECOG_PSEUDO:
          image_win->AddMessage("Recogging selected blobs");
          recog_pseudo_word(current_page_res, selection_box);
          break;

        default:
          sprintf(msg, "Mode %d not yet implemented", mode);
          image_win->AddMessage(msg);
          break;
      }
    default:
      break;
  }
}
void char_clip_word(                            //
                    WERD *word,                 //word to be processed
                    IMAGE &bin_image,           //whole image
                    PIXROW_LIST *&pixrow_list,  //pixrows built
                    IMAGELINE *&imlines,        //lines cut from image
                    TBOX &pix_box                //box defining imlines
                   ) {
  TBOX word_box = word->bounding_box ();
  PBLOB_LIST *blob_list;
  PBLOB_IT blob_it;
  PIXROW_IT pixrow_it;
  inT16 pix_offset;              //Y pos of pixrow[0]
  inT16 row_height;              //No of pix rows
  inT16 imlines_x_offset;
  PIXROW *prev;
  PIXROW *next;
  PIXROW *current;
  BOOL8 changed;                 //still improving
  BOOL8 just_changed;            //still improving
  inT16 iteration_count = 0;
  inT16 foreground_colour;

  if (word->flag (W_INVERSE))
    foreground_colour = 1;
  else
    foreground_colour = 0;

  /* Define region for max pixrow expansion */
  pix_box = word_box;
  pix_box.move_bottom_edge (-pix_word_margin);
  pix_box.move_top_edge (pix_word_margin);
  pix_box.move_left_edge (-pix_word_margin);
  pix_box.move_right_edge (pix_word_margin);
  pix_box -= TBOX (ICOORD (0, 0 + BUG_OFFSET),
    ICOORD (bin_image.get_xsize (),
    bin_image.get_ysize () - BUG_OFFSET));

  /* Generate pixrows list */

  pix_offset = pix_box.bottom ();
  row_height = pix_box.height ();
  blob_list = word->blob_list ();
  blob_it.set_to_list (blob_list);

  pixrow_list = new PIXROW_LIST;
  pixrow_it.set_to_list (pixrow_list);

  for (blob_it.mark_cycle_pt (); !blob_it.cycled_list (); blob_it.forward ()) {
    PIXROW *row = new PIXROW (pix_offset, row_height, blob_it.data ());
    ASSERT_HOST (!row->
      bad_box (bin_image.get_xsize (), bin_image.get_ysize ()));
    pixrow_it.add_after_then_move (row);
  }

  imlines = generate_imlines (bin_image, pix_box);

  /* Contract pixrows - shrink min and max back to black pixels */

  imlines_x_offset = pix_box.left ();

  pixrow_it.move_to_first ();
  for (pixrow_it.mark_cycle_pt ();
  !pixrow_it.cycled_list (); pixrow_it.forward ()) {
    ASSERT_HOST (!pixrow_it.data ()->
      bad_box (bin_image.get_xsize (), bin_image.get_ysize ()));
    pixrow_it.data ()->contract (imlines, imlines_x_offset,
      foreground_colour);
    ASSERT_HOST (!pixrow_it.data ()->
      bad_box (bin_image.get_xsize (), bin_image.get_ysize ()));
  }

  /* Expand pixrows iteratively 1 pixel at a time */
  do {
    changed = FALSE;
    pixrow_it.move_to_first ();
    prev = NULL;
    current = NULL;
    next = pixrow_it.data ();
    for (pixrow_it.mark_cycle_pt ();
    !pixrow_it.cycled_list (); pixrow_it.forward ()) {
      prev = current;
      current = next;
      if (pixrow_it.at_last ())
        next = NULL;
      else
        next = pixrow_it.data_relative (1);
      just_changed = current->extend (imlines, pix_box, prev, next,
        foreground_colour);
      ASSERT_HOST (!current->
        bad_box (bin_image.get_xsize (),
        bin_image.get_ysize ()));
      changed = changed || just_changed;
    }
    iteration_count++;
  }
  while (changed);
}
Example #25
0
/// Gather consecutive blobs that match the given box into the best_state
/// and corresponding correct_text.
///
/// Fights over which box owns which blobs are settled by pre-chopping and
/// applying the blobs to box or next_box with the least non-overlap.
/// @return false if the box was in error, which can only be caused by
/// failing to find an appropriate blob for a box.
///
/// This means that occasionally, blobs may be incorrectly segmented if the
/// chopper fails to find a suitable chop point.
bool Tesseract::ResegmentCharBox(PAGE_RES* page_res, const TBOX *prev_box,
                                 const TBOX& box, const TBOX& next_box,
                                 const char* correct_text) {
  if (applybox_debug > 1) {
    tprintf("\nAPPLY_BOX: in ResegmentCharBox() for %s\n", correct_text);
  }
  PAGE_RES_IT page_res_it(page_res);
  WERD_RES* word_res;
  for (word_res = page_res_it.word(); word_res != NULL;
       word_res = page_res_it.forward()) {
    if (!word_res->box_word->bounding_box().major_overlap(box))
      continue;
    if (applybox_debug > 1) {
      tprintf("Checking word box:");
      word_res->box_word->bounding_box().print();
    }
    int word_len = word_res->box_word->length();
    for (int i = 0; i < word_len; ++i) {
      TBOX char_box = TBOX();
      int blob_count = 0;
      for (blob_count = 0; i + blob_count < word_len; ++blob_count) {
        TBOX blob_box = word_res->box_word->BlobBox(i + blob_count);
        if (!blob_box.major_overlap(box))
          break;
        if (word_res->correct_text[i + blob_count].length() > 0)
          break;  // Blob is claimed already.
        double current_box_miss_metric = BoxMissMetric(blob_box, box);
        double next_box_miss_metric = BoxMissMetric(blob_box, next_box);
        if (applybox_debug > 2) {
          tprintf("Checking blob:");
          blob_box.print();
          tprintf("Current miss metric = %g, next = %g\n",
                  current_box_miss_metric, next_box_miss_metric);
        }
        if (current_box_miss_metric > next_box_miss_metric)
          break;  // Blob is a better match for next box.
        char_box += blob_box;
      }
      if (blob_count > 0) {
        if (applybox_debug > 1) {
          tprintf("Index [%d, %d) seem good.\n", i, i + blob_count);
        }
        if (!char_box.almost_equal(box, 3) &&
            (box.x_gap(next_box) < -3 ||
             (prev_box != NULL && prev_box->x_gap(box) < -3))) {
          return false;
        }
        // We refine just the box_word, best_state and correct_text here.
        // The rebuild_word is made in TidyUp.
        // blob_count blobs are put together to match the box. Merge the
        // box_word boxes, save the blob_count in the state and the text.
        word_res->box_word->MergeBoxes(i, i + blob_count);
        word_res->best_state[i] = blob_count;
        word_res->correct_text[i] = correct_text;
        if (applybox_debug > 2) {
          tprintf("%d Blobs match: blob box:", blob_count);
          word_res->box_word->BlobBox(i).print();
          tprintf("Matches box:");
          box.print();
          tprintf("With next box:");
          next_box.print();
        }
        // Eliminated best_state and correct_text entries for the consumed
        // blobs.
        for (int j = 1; j < blob_count; ++j) {
          word_res->best_state.remove(i + 1);
          word_res->correct_text.remove(i + 1);
        }
        // Assume that no box spans multiple source words, so we are done with
        // this box.
        if (applybox_debug > 1) {
          tprintf("Best state = ");
          for (int j = 0; j < word_res->best_state.size(); ++j) {
            tprintf("%d ", word_res->best_state[j]);
          }
          tprintf("\n");
          tprintf("Correct text = [[ ");
          for (int j = 0; j < word_res->correct_text.size(); ++j) {
            tprintf("%s ", word_res->correct_text[j].string());
          }
          tprintf("]]\n");
        }
        return true;
      }
    }
  }
  if (applybox_debug > 0) {
    tprintf("FAIL!\n");
  }
  return false;  // Failure.
}
Example #26
0
C_OUTLINE::C_OUTLINE(                     //constructor
                     C_OUTLINE *srcline,  //outline to
                     FCOORD rotation      //rotate
                    ) : offsets(NULL) {
  TBOX new_box;                   //easy bounding
  inT16 stepindex;               //index to step
  inT16 dirdiff;                 //direction change
  ICOORD pos;                    //current position
  ICOORD prevpos;                //previous dest point

  ICOORD destpos;                //destination point
  inT16 destindex;               //index to step
  DIR128 dir;                    //coded direction
  uinT8 new_step;

  stepcount = srcline->stepcount * 2;
  if (stepcount == 0) {
    steps = NULL;
    box = srcline->box;
    box.rotate(rotation);
    return;
  }
                                 //get memory
  steps = (uinT8 *) alloc_mem (step_mem());
  memset(steps, 0, step_mem());

  for (int iteration = 0; iteration < 2; ++iteration) {
    DIR128 round1 = iteration == 0 ? 32 : 0;
    DIR128 round2 = iteration != 0 ? 32 : 0;
    pos = srcline->start;
    prevpos = pos;
    prevpos.rotate (rotation);
    start = prevpos;
    box = TBOX (start, start);
    destindex = 0;
    for (stepindex = 0; stepindex < srcline->stepcount; stepindex++) {
      pos += srcline->step (stepindex);
      destpos = pos;
      destpos.rotate (rotation);
      //  tprintf("%i %i %i %i ", destpos.x(), destpos.y(), pos.x(), pos.y());
      while (destpos.x () != prevpos.x () || destpos.y () != prevpos.y ()) {
        dir = DIR128 (FCOORD (destpos - prevpos));
        dir += 64;                 //turn to step style
        new_step = dir.get_dir ();
        //  tprintf(" %i\n", new_step);
        if (new_step & 31) {
          set_step(destindex++, dir + round1);
          prevpos += step(destindex - 1);
          if (destindex < 2
            || ((dirdiff =
            step_dir (destindex - 1) - step_dir (destindex - 2)) !=
            -64 && dirdiff != 64)) {
            set_step(destindex++, dir + round2);
            prevpos += step(destindex - 1);
          } else {
            prevpos -= step(destindex - 1);
            destindex--;
            prevpos -= step(destindex - 1);
            set_step(destindex - 1, dir + round2);
            prevpos += step(destindex - 1);
          }
        }
        else {
          set_step(destindex++, dir);
          prevpos += step(destindex - 1);
        }
        while (destindex >= 2 &&
               ((dirdiff =
                 step_dir (destindex - 1) - step_dir (destindex - 2)) == -64 ||
                dirdiff == 64)) {
          prevpos -= step(destindex - 1);
          prevpos -= step(destindex - 2);
          destindex -= 2;        // Forget u turn
        }
        //ASSERT_HOST(prevpos.x() == destpos.x() && prevpos.y() == destpos.y());
        new_box = TBOX (destpos, destpos);
        box += new_box;
      }
    }
    ASSERT_HOST (destpos.x () == start.x () && destpos.y () == start.y ());
    dirdiff = step_dir (destindex - 1) - step_dir (0);
    while ((dirdiff == 64 || dirdiff == -64) && destindex > 1) {
      start += step (0);
      destindex -= 2;
      for (int i = 0; i < destindex; ++i)
        set_step(i, step_dir(i + 1));
      dirdiff = step_dir (destindex - 1) - step_dir (0);
    }
    if (destindex >= 4)
      break;
  }
  ASSERT_HOST(destindex <= stepcount);
  stepcount = destindex;
  destpos = start;
  for (stepindex = 0; stepindex < stepcount; stepindex++) {
    destpos += step (stepindex);
  }
  ASSERT_HOST (destpos.x () == start.x () && destpos.y () == start.y ());
}
Example #27
0
TBOX TESSLINE::bounding_box() const {
  return TBOX(topleft.x, botright.y, botright.x, topleft.y);
}