// Copies the given feature_space and uses it as the index feature map
// from INT_FEATURE_STRUCT.
void IntFeatureMap::Init(const IntFeatureSpace& feature_space) {
  feature_space_ = feature_space;
  mapping_changed_ = false;
  int sparse_size = feature_space_.Size();
  feature_map_.Init(sparse_size, true);
  feature_map_.Setup();
  compact_size_ = feature_map_.CompactSize();
  // Initialize look-up tables if needed.
  FCOORD dir = FeatureDirection(0);
  if (dir.x() == 0.0f && dir.y() == 0.0f)
    InitIntegerFX();
  // Compute look-up tables to generate offset features.
  for (int dir = 0; dir < kNumOffsetMaps; ++dir) {
    delete [] offset_plus_[dir];
    delete [] offset_minus_[dir];
    offset_plus_[dir] = new int[sparse_size];
    offset_minus_[dir] = new int[sparse_size];
  }
  for (int dir = 1; dir <= kNumOffsetMaps; ++dir) {
    for (int i = 0; i < sparse_size; ++i) {
      int offset_index = ComputeOffsetFeature(i, dir);
      offset_plus_[dir - 1][i] = offset_index;
      offset_index = ComputeOffsetFeature(i, -dir);
      offset_minus_[dir - 1][i] = offset_index;
    }
  }
}
Example #2
0
// Rotate the grid by rotation, keeping cell contents.
// rotation must be a multiple of 90 degrees.
// NOTE: due to partial cells, cell coverage in the rotated grid will be
// inexact. This is why there is no Rotate for the generic BBGrid.
// TODO(rays) investigate fixing this inaccuracy by moving the origin after
// rotation.
void IntGrid::Rotate(const FCOORD& rotation) {
  ASSERT_HOST(rotation.x() == 0.0f || rotation.y() == 0.0f);
  ICOORD old_bleft(bleft());
  ICOORD old_tright(tright());
  int old_width = gridwidth();
  int old_height = gridheight();
  TBOX box(bleft(), tright());
  box.rotate(rotation);
  int* old_grid = grid_;
  grid_ = NULL;
  Init(gridsize(), box.botleft(), box.topright());
  // Iterate over the old grid, copying data to the rotated position in the new.
  int oldi = 0;
  FCOORD x_step(rotation);
  x_step *= gridsize();
  for (int oldy = 0; oldy < old_height; ++oldy) {
    FCOORD line_pos(old_bleft.x(), old_bleft.y() + gridsize() * oldy);
    line_pos.rotate(rotation);
    for (int oldx = 0; oldx < old_width; ++oldx, line_pos += x_step, ++oldi) {
      int grid_x, grid_y;
      GridCoords(static_cast<int>(line_pos.x() + 0.5),
                 static_cast<int>(line_pos.y() + 0.5),
                 &grid_x, &grid_y);
      grid_[grid_y * gridwidth() + grid_x] = old_grid[oldi];
    }
  }
  delete [] old_grid;
}
inT16 OUTLINE::winding_number(                     //winding number
                              const FCOORD &point  //point to wind around
                             ) {
  inT16 count;                   //winding count
  POLYPT *polypt;                //current point
  FCOORD vec;                    //to current point
  float cross;                   //cross product
  POLYPT_IT it = &outline;       //iterator

  count = 0;
  do {
    polypt = it.data ();
    vec = polypt->pos - point;
                                 //crossing the line
    if (vec.y () <= 0 && vec.y () + polypt->vec.y () > 0) {
      cross = vec * polypt->vec; //cross product
      if (cross > 0)
        count++;                 //crossing right half
      else if (cross == 0)
        return INTERSECTING;     //going through point
    }
    else if (vec.y () > 0 && vec.y () + polypt->vec.y () <= 0) {
      cross = vec * polypt->vec;
      if (cross < 0)
        count--;                 //crossing back
      else if (cross == 0)
        return INTERSECTING;     //illegal
    }
    it.forward ();
  }
  while (!it.at_first ());
  return count;                  //winding number
}
Example #4
0
void DENORM::LocalNormTransform(const FCOORD& pt, FCOORD* transformed) const {
  FCOORD translated(pt.x() - x_origin_, pt.y() - YOriginAtOrigX(pt.x()));
  translated.set_x(translated.x() * x_scale_);
  translated.set_y(translated.y() * YScaleAtOrigX(pt.x()));
  if (rotation_ != NULL)
    translated.rotate(*rotation_);
  transformed->set_x(translated.x() + final_xshift_);
  transformed->set_y(translated.y() + final_yshift_);
}
Example #5
0
// Rotates by the given rotation in place.
void TESSLINE::Rotate(const FCOORD rot) {
  EDGEPT* pt = loop;
  do {
    int tmp = static_cast<int>(floor(pt->pos.x * rot.x() -
                                     pt->pos.y * rot.y() + 0.5));
    pt->pos.y = static_cast<int>(floor(pt->pos.y * rot.x() +
                                       pt->pos.x * rot.y() + 0.5));
    pt->pos.x = tmp;
    pt = pt->next;
  } while (pt != loop);
  SetupFromPos();
}
Example #6
0
void DENORM::LocalDenormTransform(const FCOORD& pt, FCOORD* original) const {
  FCOORD rotated(pt.x() - final_xshift_, pt.y() - final_yshift_);
  if (rotation_ != NULL) {
    FCOORD inverse_rotation(rotation_->x(), -rotation_->y());
    rotated.rotate(inverse_rotation);
  }
  original->set_x(rotated.x() / x_scale_ + x_origin_);
  float y_scale = y_scale_;
  if (num_segs_ > 0)
    y_scale = YScaleAtOrigX(original->x());
  original->set_y(rotated.y() / y_scale + YOriginAtOrigX(original->x()));
}
Example #7
0
// Gathers outline points and their directions from start_index into dirs by
// stepping along the outline and normalizing the coordinates until the
// required feature_length has been collected or end_index is reached.
// On input pos must point to the position corresponding to start_index and on
// return pos is updated to the current raw position, and pos_normed is set to
// the normed version of pos.
// Since directions wrap-around, they need special treatment to get the mean.
// Provided the cluster of directions doesn't straddle the wrap-around point,
// the simple mean works. If they do, then, unless the directions are wildly
// varying, the cluster rotated by 180 degrees will not straddle the wrap-
// around point, so mean(dir + 180 degrees) - 180 degrees will work. Since
// LLSQ conveniently stores the mean of 2 variables, we use it to store
// dir and dir+128 (128 is 180 degrees) and then use the resulting mean
// with the least variance.
static int GatherPoints(const C_OUTLINE* outline, double feature_length,
                        const DENORM& denorm, const DENORM* root_denorm,
                        int start_index, int end_index,
                        ICOORD* pos, FCOORD* pos_normed,
                        LLSQ* points, LLSQ* dirs) {
  int step_length = outline->pathlength();
  ICOORD step = outline->step(start_index % step_length);
  // Prev_normed is the start point of this collection and will be set on the
  // first iteration, and on later iterations used to determine the length
  // that has been collected.
  FCOORD prev_normed;
  points->clear();
  dirs->clear();
  int num_points = 0;
  int index;
  for (index = start_index; index <= end_index; ++index, *pos += step) {
    step = outline->step(index % step_length);
    int edge_weight = outline->edge_strength_at_index(index % step_length);
    if (edge_weight == 0) {
      // This point has conflicting gradient and step direction, so ignore it.
      continue;
    }
    // Get the sub-pixel precise location and normalize.
    FCOORD f_pos = outline->sub_pixel_pos_at_index(*pos, index % step_length);
    denorm.NormTransform(root_denorm, f_pos, pos_normed);
    if (num_points == 0) {
      // The start of this segment.
      prev_normed = *pos_normed;
    } else {
      FCOORD offset = *pos_normed - prev_normed;
      float length = offset.length();
      if (length > feature_length) {
        // We have gone far enough from the start. We will use this point in
        // the next set so return what we have so far.
        return index;
      }
    }
    points->add(pos_normed->x(), pos_normed->y(), edge_weight);
    int direction = outline->direction_at_index(index % step_length);
    if (direction >= 0) {
      direction = NormalizeDirection(direction, f_pos, denorm, root_denorm);
      // Use both the direction and direction +128 so we are not trying to
      // take the mean of something straddling the wrap-around point.
      dirs->add(direction, Modulo(direction + 128, 256));
    }
    ++num_points;
  }
  return index;
}
Example #8
0
void DENORM::LocalNormTransform(const FCOORD& pt, FCOORD* transformed) const {
  FCOORD translated(pt.x() - x_origin_, pt.y() - y_origin_);
  if (x_map_ != NULL && y_map_ != NULL) {
    int x = ClipToRange(IntCastRounded(translated.x()), 0, x_map_->size()-1);
    translated.set_x((*x_map_)[x]);
    int y = ClipToRange(IntCastRounded(translated.y()), 0, y_map_->size()-1);
    translated.set_y((*y_map_)[y]);
  } else {
    translated.set_x(translated.x() * x_scale_);
    translated.set_y(translated.y() * y_scale_);
    if (rotation_ != NULL)
      translated.rotate(*rotation_);
  }
  transformed->set_x(translated.x() + final_xshift_);
  transformed->set_y(translated.y() + final_yshift_);
}
// Inserts a list of blobs into the projection.
// Rotation is a multiple of 90 degrees to get from blob coords to
// nontext_map coords, nontext_map_box is the bounds of the nontext_map.
// Blobs are spread horizontally or vertically according to their internal
// flags, but the spreading is truncated by set pixels in the nontext_map
// and also by the horizontal rule line limits on the blobs.
void TextlineProjection::ProjectBlobs(BLOBNBOX_LIST* blobs,
                                      const FCOORD& rotation,
                                      const TBOX& nontext_map_box,
                                      Pix* nontext_map) {
  BLOBNBOX_IT blob_it(blobs);
  for (blob_it.mark_cycle_pt(); !blob_it.cycled_list(); blob_it.forward()) {
    BLOBNBOX* blob = blob_it.data();
    TBOX bbox = blob->bounding_box();
    ICOORD middle((bbox.left() + bbox.right()) / 2,
                  (bbox.bottom() + bbox.top()) / 2);
    bool spreading_horizontally = PadBlobBox(blob, &bbox);
    // Rotate to match the nontext_map.
    bbox.rotate(rotation);
    middle.rotate(rotation);
    if (rotation.x() == 0.0f)
      spreading_horizontally = !spreading_horizontally;
    // Clip to the image before applying the increments.
    bbox &= nontext_map_box;  // This is in-place box intersection.
    // Check for image pixels before spreading.
    TruncateBoxToMissNonText(middle.x(), middle.y(), spreading_horizontally,
                             nontext_map, &bbox);
    if (bbox.area() > 0) {
      IncrementRectangle8Bit(bbox);
    }
  }
}
Example #10
0
void DENORM::LocalDenormTransform(const FCOORD& pt, FCOORD* original) const {
  FCOORD rotated(pt.x() - final_xshift_, pt.y() - final_yshift_);
  if (x_map_ != NULL && y_map_ != NULL) {
    int x = x_map_->binary_search(rotated.x());
    original->set_x(x + x_origin_);
    int y = y_map_->binary_search(rotated.y());
    original->set_y(y + y_origin_);
  } else {
    if (rotation_ != NULL) {
      FCOORD inverse_rotation(rotation_->x(), -rotation_->y());
      rotated.rotate(inverse_rotation);
    }
    original->set_x(rotated.x() / x_scale_ + x_origin_);
    float y_scale = y_scale_;
    original->set_y(rotated.y() / y_scale + y_origin_);
  }
}
Example #11
0
void PageIterator::Orientation(tesseract::Orientation *orientation,
                               tesseract::WritingDirection *writing_direction,
                               tesseract::TextlineOrder *textline_order,
                               float *deskew_angle) const {
  BLOCK* block = it_->block()->block;

  // Orientation
  FCOORD up_in_image(0.0, 1.0);
  up_in_image.unrotate(block->classify_rotation());
  up_in_image.rotate(block->re_rotation());

  if (up_in_image.x() == 0.0F) {
    if (up_in_image.y() > 0.0F) {
      *orientation = ORIENTATION_PAGE_UP;
    } else {
      *orientation = ORIENTATION_PAGE_DOWN;
    }
  } else if (up_in_image.x() > 0.0F) {
    *orientation = ORIENTATION_PAGE_RIGHT;
  } else {
    *orientation = ORIENTATION_PAGE_LEFT;
  }

  // Writing direction
  bool is_vertical_text = (block->classify_rotation().x() == 0.0);
  bool right_to_left = block->right_to_left();
  *writing_direction =
      is_vertical_text
          ? WRITING_DIRECTION_TOP_TO_BOTTOM
          : (right_to_left
                ? WRITING_DIRECTION_RIGHT_TO_LEFT
                : WRITING_DIRECTION_LEFT_TO_RIGHT);

  // Textline Order
  bool is_mongolian = false;  // TODO(eger): fix me
  *textline_order = is_vertical_text
      ? (is_mongolian
         ? TEXTLINE_ORDER_LEFT_TO_RIGHT
         : TEXTLINE_ORDER_RIGHT_TO_LEFT)
      : TEXTLINE_ORDER_TOP_TO_BOTTOM;

  // Deskew angle
  FCOORD skew = block->skew();  // true horizontal for textlines
  *deskew_angle = -skew.angle();
}
Example #12
0
// Fits a line in the given direction to blobs that are close to the given
// target_offset perpendicular displacement from the direction. The fit
// error is allowed to be cheat_allowance worse than the existing fit, and
// will still be used.
// If cheat_allowance > 0, the new fit will be good and replace the current
// fit if it has better fit (with cheat) OR its error is below
// max_baseline_error_ and the old fit is marked bad.
// Otherwise the new fit will only replace the old if it is really better,
// or the old fit is marked bad and the new fit has sufficient points, as
// well as being within the max_baseline_error_.
void BaselineRow::FitConstrainedIfBetter(int debug,
                                         const FCOORD& direction,
                                         double cheat_allowance,
                                         double target_offset) {
  double halfrange = fit_halfrange_ * direction.length();
  double min_dist = target_offset - halfrange;
  double max_dist = target_offset + halfrange;
  ICOORD line_pt;
  double new_error = fitter_.ConstrainedFit(direction, min_dist, max_dist,
                                            debug > 2, &line_pt);
  // Allow cheat_allowance off the new error
  new_error -= cheat_allowance;
  double old_angle = BaselineAngle();
  double new_angle = direction.angle();
  if (debug > 1) {
    tprintf("Constrained error = %g, original = %g",
            new_error, baseline_error_);
    tprintf(" angles = %g, %g, delta=%g vs threshold %g\n",
            old_angle, new_angle,
            new_angle - old_angle, kMaxSkewDeviation);
  }
  bool new_good_baseline = new_error <= max_baseline_error_ &&
      (cheat_allowance > 0.0 || fitter_.SufficientPointsForIndependentFit());
  // The new will replace the old if any are true:
  // 1. the new error is better
  // 2. the old is NOT good, but the new is
  // 3. there is a wild angular difference between them (assuming that the new
  //    is a better guess at the angle.)
  if (new_error <= baseline_error_ ||
      (!good_baseline_ && new_good_baseline) ||
      fabs(new_angle - old_angle) > kMaxSkewDeviation) {
    baseline_error_ = new_error;
    baseline_pt1_ = line_pt;
    baseline_pt2_ = baseline_pt1_ + direction;
    good_baseline_ = new_good_baseline;
    if (debug > 1) {
      tprintf("Replacing with constrained baseline, good = %d\n",
              good_baseline_);
    }
  } else if (debug > 1) {
    tprintf("Keeping old baseline\n");
  }
}
Example #13
0
// Returns the point on the given line nearest to this, ie the point such
// that the vector point->this is perpendicular to the line.
// The line is defined as a line_point and a dir_vector for its direction.
FCOORD FCOORD::nearest_pt_on_line(const FCOORD& line_point,
                                  const FCOORD& dir_vector) const {
  FCOORD point_vector(*this - line_point);
  // The dot product (%) is |dir_vector||point_vector|cos theta, so dividing by
  // the square of the length of dir_vector gives us the fraction of dir_vector
  // to add to line1 to get the appropriate point, so
  // result = line1 + lambda dir_vector.
  double lambda = point_vector % dir_vector / dir_vector.sqlength();
  return line_point + (dir_vector * lambda);
}
void OUTLINE::move(                  // reposition OUTLINE
                   const FCOORD vec  // by vector
                  ) {
                                 //child outline itertr
  OUTLINE_IT child_it(&children);
  POLYPT_IT poly_it(&outline);  //outline point itertr

  box.move (vec);

  start.set_x ((inT16) floor (start.x () + vec.x () + 0.5));
  // ?? Why ICOORD?
  start.set_y ((inT16) floor (start.y () + vec.y () + 0.5));
  // ?? Why ICOORD?

  for (poly_it.mark_cycle_pt (); !poly_it.cycled_list (); poly_it.forward ())
    poly_it.data ()->pos += vec;

  for (child_it.mark_cycle_pt (); !child_it.cycled_list ();
    child_it.forward ())
  child_it.data ()->move (vec);  // move child outlines
}
void OUTLINE::scale(                     // scale OUTLINE
                    const FCOORD vector  //by fcoord
                   ) {
                                 //child outline itertr
  OUTLINE_IT child_it(&children);
  POLYPT_IT poly_it(&outline);  //outline point itertr
  POLYPT *pt;

  box.scale (vector);

  start.set_x ((inT16) floor (start.x () * vector.x () + 0.5));
  // ?? Why ICOORD?
  start.set_y ((inT16) floor (start.y () * vector.y () + 0.5));
  // ?? Why ICOORD?

  for (poly_it.mark_cycle_pt (); !poly_it.cycled_list (); poly_it.forward ()) {
    pt = poly_it.data ();
    pt->pos =
      FCOORD (pt->pos.x () * vector.x (), pt->pos.y () * vector.y ());
    pt->vec =
      FCOORD (pt->vec.x () * vector.x (), pt->vec.y () * vector.y ());
  }

  for (child_it.mark_cycle_pt (); !child_it.cycled_list ();
    child_it.forward ())
                                 //scale child outlines
  child_it.data ()->scale (vector);
}
Example #16
0
DIR128::DIR128(                 //from fcoord
               const FCOORD fc  //vector to quantize
              ) {
  int high, low, current;        //binary search

  low = 0;
  if (fc.y () == 0) {
    if (fc.x () >= 0)
      dir = 0;
    else
      dir = MODULUS / 2;
    return;
  }
  high = MODULUS;
  do {
    current = (high + low) / 2;
    if (dirtab[current] * fc >= 0)
      low = current;
    else
      high = current;
  }
  while (high - low > 1);
  dir = low;
}
Example #17
0
// Rotates the box by the angle given by rotation.
// If the blob is a diacritic, then only small rotations for skew
// correction can be applied.
void BLOBNBOX::rotate_box(FCOORD rotation) {
  if (IsDiacritic()) {
    ASSERT_HOST(rotation.x() >= kCosSmallAngle)
    ICOORD top_pt((box.left() + box.right()) / 2, base_char_top_);
    ICOORD bottom_pt(top_pt.x(), base_char_bottom_);
    top_pt.rotate(rotation);
    base_char_top_ = top_pt.y();
    bottom_pt.rotate(rotation);
    base_char_bottom_ = bottom_pt.y();
    box.rotate(rotation);
  } else {
    box.rotate(rotation);
    set_diacritic_box(box);
  }
}
Example #18
0
// Draws the features in the given window.
void WordFeature::Draw(const GenericVector<WordFeature>& features,
                       ScrollView* window) {
  for (int f = 0; f < features.size(); ++f) {
    FCOORD pos(features[f].x_, features[f].y_);
    FCOORD dir;
    dir.from_direction(features[f].dir_);
    dir *= 8.0f;
    window->SetCursor(IntCastRounded(pos.x() - dir.x()),
                      IntCastRounded(pos.y() - dir.y()));
    window->DrawTo(IntCastRounded(pos.x() + dir.x()),
                      IntCastRounded(pos.y() + dir.y()));
  }
}
Example #19
0
void POLY_BLOCK::rotate(FCOORD rotation) {
  FCOORD pos;                    //current pos;
  ICOORDELT *pt;                 //current point
  ICOORDELT_IT pts = &vertices;  //iterator

  do {
    pt = pts.data ();
    pos.set_x (pt->x ());
    pos.set_y (pt->y ());
    pos.rotate (rotation);
    pt->set_x ((inT16) (floor (pos.x () + 0.5)));
    pt->set_y ((inT16) (floor (pos.y () + 0.5)));
    pts.forward ();
  }
  while (!pts.at_first ());
  compute_bb();
}
Example #20
0
// Adds any edges from a single segment of outline between pt1 and pt2 to
// the x_coords, y_coords vectors. pt1 and pt2 should be relative to the
// bottom-left of the bounding box, hence indices to x_coords, y_coords
// are clipped to ([0,x_limit], [0,y_limit]).
// See GetEdgeCoords above for a description of x_coords, y_coords.
static void SegmentCoords(const FCOORD& pt1, const FCOORD& pt2,
                          int x_limit, int y_limit,
                          GenericVector<GenericVector<int> >* x_coords,
                          GenericVector<GenericVector<int> >* y_coords) {
  FCOORD step(pt2);
  step -= pt1;
  int start = ClipToRange(IntCastRounded(MIN(pt1.x(), pt2.x())), 0, x_limit);
  int end = ClipToRange(IntCastRounded(MAX(pt1.x(), pt2.x())), 0, x_limit);
  for (int x = start; x < end; ++x) {
    int y = IntCastRounded(pt1.y() + step.y() * (x + 0.5 - pt1.x()) / step.x());
    (*y_coords)[x].push_back(y);
  }
  start = ClipToRange(IntCastRounded(MIN(pt1.y(), pt2.y())), 0, y_limit);
  end = ClipToRange(IntCastRounded(MAX(pt1.y(), pt2.y())), 0, y_limit);
  for (int y = start; y < end; ++y) {
    int x = IntCastRounded(pt1.x() + step.x() * (y + 0.5 - pt1.y()) / step.y());
    (*x_coords)[y].push_back(x);
  }
}
Example #21
0
/*************************************************************************
 * PIXROW::PIXROW()
 *
 * Constructor for a specified size PIXROW from a blob
 *************************************************************************/
PIXROW::PIXROW(INT16 pos, INT16 count, PBLOB *blob) { 
  OUTLINE_LIST *outline_list;
  OUTLINE_IT outline_it;
  POLYPT_LIST *pts_list;
  POLYPT_IT pts_it;
  INT16 i;
  FCOORD pt;
  FCOORD vec;
  float y_coord;
  INT16 x_coord;

  row_offset = pos;
  row_count = count;
  min = (INT16 *) alloc_mem (count * sizeof (INT16));
  max = (INT16 *) alloc_mem (count * sizeof (INT16));
  outline_list = blob->out_list ();
  outline_it.set_to_list (outline_list);

  for (i = 0; i < count; i++) {
    min[i] = MAX_INT16 - 1;
    max[i] = -MAX_INT16 + 1;
    y_coord = row_offset + i + 0.5;
    for (outline_it.mark_cycle_pt ();
    !outline_it.cycled_list (); outline_it.forward ()) {
      pts_list = outline_it.data ()->polypts ();
      pts_it.set_to_list (pts_list);
      for (pts_it.mark_cycle_pt ();
      !pts_it.cycled_list (); pts_it.forward ()) {
        pt = pts_it.data ()->pos;
        vec = pts_it.data ()->vec;
        if ((vec.y () != 0) &&
          (((pt.y () <= y_coord) && (pt.y () + vec.y () >= y_coord))
          || ((pt.y () >= y_coord)
        && (pt.y () + vec.y () <= y_coord)))) {
          /* The segment crosses y_coord so find x-point and check for min/max. */
          x_coord = (INT16) floor ((y_coord -
            pt.y ()) * vec.x () / vec.y () +
            pt.x () + 0.5);
          if (x_coord < min[i])
            min[i] = x_coord;
          x_coord--;             //to get pix to left of line
          if (x_coord > max[i])
            max[i] = x_coord;
        }
      }
    }
  }
}
Example #22
0
// Computes and returns the displacement of the center of the line
// perpendicular to the given direction.
double BaselineRow::PerpDisp(const FCOORD& direction) const {
  float middle_x = (bounding_box_.left() + bounding_box_.right()) / 2.0f;
  FCOORD middle_pos(middle_x, StraightYAtX(middle_x));
  return direction * middle_pos / direction.length();
}
Example #23
0
WordFeature::WordFeature(const FCOORD& fcoord, uinT8 dir)
  : x_(IntCastRounded(fcoord.x())),
    y_(ClipToRange(IntCastRounded(fcoord.y()), 0, MAX_UINT8)),
    dir_(dir) {
}
Example #24
0
// Accumulates the segment between pt1 and pt2 in the LLSQ, quantizing over
// the integer coordinate grid to properly weight long vectors.
static void SegmentLLSQ(const FCOORD& pt1, const FCOORD& pt2,
                        LLSQ* accumulator) {
  FCOORD step(pt2);
  step -= pt1;
  int xstart = IntCastRounded(MIN(pt1.x(), pt2.x()));
  int xend = IntCastRounded(MAX(pt1.x(), pt2.x()));
  int ystart = IntCastRounded(MIN(pt1.y(), pt2.y()));
  int yend = IntCastRounded(MAX(pt1.y(), pt2.y()));
  if (xstart == xend && ystart == yend) return;  // Nothing to do.
  double weight = step.length() / (xend - xstart + yend - ystart);
  // Compute and save the y-position at the middle of each x-step.
  for (int x = xstart; x < xend; ++x) {
    double y = pt1.y() + step.y() * (x + 0.5 - pt1.x()) / step.x();
    accumulator->add(x + 0.5, y, weight);
  }
  // Compute and save the x-position at the middle of each y-step.
  for (int y = ystart; y < yend; ++y) {
    double x = pt1.x() + step.x() * (y + 0.5 - pt1.y()) / step.y();
    accumulator->add(x, y + 0.5, weight);
  }
}
Example #25
0
void find_blob_limits(                  //get y limits
                      PBLOB *blob,      //blob to search
                      float leftx,      //x limits
                      float rightx,
                      FCOORD rotation,  //for landscape
                      float &ymin,      //output y limits
                      float &ymax) {
  float testy;                   //y intercept
  FCOORD pos;                    //rotated
  FCOORD vec;
  POLYPT *polypt;                //current point
                                 //outlines
  OUTLINE_IT out_it = blob->out_list ();
  POLYPT_IT poly_it;             //outline pts

  ymin = (float) MAX_INT32;
  ymax = (float) -MAX_INT32;
  for (out_it.mark_cycle_pt (); !out_it.cycled_list (); out_it.forward ()) {
                                 //get points
    poly_it.set_to_list (out_it.data ()->polypts ());
    for (poly_it.mark_cycle_pt (); !poly_it.cycled_list ();
    poly_it.forward ()) {
      polypt = poly_it.data ();
      pos = polypt->pos;
      pos.rotate (rotation);
      vec = polypt->vec;
      vec.rotate (rotation);
      if (pos.x () < leftx && pos.x () + vec.x () > leftx
      || pos.x () > leftx && pos.x () + vec.x () < leftx) {
        testy = pos.y () + vec.y () * (leftx - pos.x ()) / vec.x ();
        //intercept of boundary
        if (testy < ymin)
          ymin = testy;
        if (testy > ymax)
          ymax = testy;
      }
      if (pos.x () >= leftx && pos.x () <= rightx) {
        if (pos.y () > ymax)
          ymax = pos.y ();
        if (pos.y () < ymin)
          ymin = pos.y ();
      }
      if (pos.x () > rightx && pos.x () + vec.x () < rightx
      || pos.x () < rightx && pos.x () + vec.x () > rightx) {
        testy = pos.y () + vec.y () * (rightx - pos.x ()) / vec.x ();
        //intercept of boundary
        if (testy < ymin)
          ymin = testy;
        if (testy > ymax)
          ymax = testy;
      }
    }
  }
}
Example #26
0
// Adds any edges from a single segment of outline between pt1 and pt2 to
// the bbox such that it guarantees to contain anything produced by
// SegmentCoords.
static void SegmentBBox(const FCOORD& pt1, const FCOORD& pt2, TBOX* bbox) {
  FCOORD step(pt2);
  step -= pt1;
  int x1 = IntCastRounded(MIN(pt1.x(), pt2.x()));
  int x2 = IntCastRounded(MAX(pt1.x(), pt2.x()));
  if (x2 > x1) {
    int y1 = IntCastRounded(pt1.y() + step.y() * (x1 + 0.5 - pt1.x()) /
                            step.x());
    int y2 = IntCastRounded(pt1.y() + step.y() * (x2 - 0.5 - pt1.x()) /
                            step.x());
    TBOX point(x1, MIN(y1, y2), x2, MAX(y1, y2));
    *bbox += point;
  }
  int y1 = IntCastRounded(MIN(pt1.y(), pt2.y()));
  int y2 = IntCastRounded(MAX(pt1.y(), pt2.y()));
  if (y2 > y1) {
    int x1 = IntCastRounded(pt1.x() + step.x() * (y1 + 0.5 - pt1.y()) /
                            step.y());
    int x2 = IntCastRounded(pt1.x() + step.x() * (y2 - 0.5 - pt1.y()) /
                            step.y());
    TBOX point(MIN(x1, x2), y1, MAX(x1, x2), y2);
    *bbox += point;
  }
}
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 #28
0
// Groups blocks by rotation, then, for each group, makes a WordGrid and calls
// TransferDiacriticsToWords to copy the diacritic blobs to the most
// appropriate words in the group of blocks. Source blobs are not touched.
void Textord::TransferDiacriticsToBlockGroups(BLOBNBOX_LIST* diacritic_blobs,
                                              BLOCK_LIST* blocks) {
  // Angle difference larger than this is too much to consider equal.
  // They should only be in multiples of M_PI/2 anyway.
  const double kMaxAngleDiff = 0.01;  // About 0.6 degrees.
  PointerVector<BlockGroup> groups;
  BLOCK_IT bk_it(blocks);
  for (bk_it.mark_cycle_pt(); !bk_it.cycled_list(); bk_it.forward()) {
    BLOCK* block = bk_it.data();
    if (block->pdblk.poly_block() != nullptr && !block->pdblk.poly_block()->IsText()) {
      continue;
    }
    // Linear search of the groups to find a matching rotation.
    float block_angle = block->re_rotation().angle();
    int best_g = 0;
    float best_angle_diff = MAX_FLOAT32;
    for (int g = 0; g < groups.size(); ++g) {
      double angle_diff = fabs(block_angle - groups[g]->angle);
      if (angle_diff > M_PI) angle_diff = fabs(angle_diff - 2.0 * M_PI);
      if (angle_diff < best_angle_diff) {
        best_angle_diff = angle_diff;
        best_g = g;
      }
    }
    if (best_angle_diff > kMaxAngleDiff) {
      groups.push_back(new BlockGroup(block));
    } else {
      groups[best_g]->blocks.push_back(block);
      groups[best_g]->bounding_box += block->pdblk.bounding_box();
      float x_height = block->x_height();
      if (x_height < groups[best_g]->min_xheight)
        groups[best_g]->min_xheight = x_height;
    }
  }
  // Now process each group of blocks.
  PointerVector<WordWithBox> word_ptrs;
  for (int g = 0; g < groups.size(); ++g) {
    const BlockGroup* group = groups[g];
    if (group->bounding_box.null_box()) continue;
    WordGrid word_grid(group->min_xheight, group->bounding_box.botleft(),
                       group->bounding_box.topright());
    for (int b = 0; b < group->blocks.size(); ++b) {
      ROW_IT row_it(group->blocks[b]->row_list());
      for (row_it.mark_cycle_pt(); !row_it.cycled_list(); row_it.forward()) {
        ROW* row = row_it.data();
        // Put the words of the row into the grid.
        WERD_IT w_it(row->word_list());
        for (w_it.mark_cycle_pt(); !w_it.cycled_list(); w_it.forward()) {
          WERD* word = w_it.data();
          WordWithBox* box_word = new WordWithBox(word);
          word_grid.InsertBBox(true, true, box_word);
          // Save the pointer where it will be auto-deleted.
          word_ptrs.push_back(box_word);
        }
      }
    }
    FCOORD rotation = group->rotation;
    // Make it a forward rotation that will transform blob coords to block.
    rotation.set_y(-rotation.y());
    TransferDiacriticsToWords(diacritic_blobs, rotation, &word_grid);
  }
}
Example #29
0
// Returns the direction of the fitted line as a unit vector, using the
// least mean squared perpendicular distance. The line runs through the
// mean_point, i.e. a point p on the line is given by:
// p = mean_point() + lambda * vector_fit() for some real number lambda.
// Note that the result (0<=x<=1, -1<=y<=-1) is directionally ambiguous
// and may be negated without changing its meaning.
FCOORD LLSQ::vector_fit() const {
  double x_var = x_variance();
  double y_var = y_variance();
  double covar = covariance();
  FCOORD result;
  if (x_var >= y_var) {
    if (x_var == 0.0)
      return FCOORD(0.0f, 0.0f);
    result.set_x(x_var / sqrt(x_var * x_var + covar * covar));
    result.set_y(sqrt(1.0 - result.x() * result.x()));
  } else {
    result.set_y(y_var / sqrt(y_var * y_var + covar * covar));
    result.set_x(sqrt(1.0 - result.y() * result.y()));
  }
  if (covar < 0.0)
    result.set_y(-result.y());
  return result;
}