void save_chop_cfragment( //chop the outline int16_t head_index, //head of fragment ICOORD head_pos, //head of fragment int16_t tail_index, //tail of fragment ICOORD tail_pos, //tail of fragment C_OUTLINE *srcline, //source of edgesteps C_OUTLINE_FRAG_LIST *frags //fragment list ) { int16_t jump; //gap across end int16_t stepcount; //total steps C_OUTLINE_FRAG *head; //head of fragment C_OUTLINE_FRAG *tail; //tail of fragment int16_t tail_y; //ycoord of tail ASSERT_HOST (tail_pos.x () == head_pos.x ()); ASSERT_HOST (tail_index != head_index); stepcount = tail_index - head_index; if (stepcount < 0) stepcount += srcline->pathlength (); jump = tail_pos.y () - head_pos.y (); if (jump < 0) jump = -jump; if (jump == stepcount) return; //its a nop tail_y = tail_pos.y (); head = new C_OUTLINE_FRAG (head_pos, tail_pos, srcline, head_index, tail_index); tail = new C_OUTLINE_FRAG (head, tail_y); head->other_end = tail; add_frag_to_list(head, frags); add_frag_to_list(tail, frags); }
void C_OUTLINE::plot( //draw it ScrollView* window, // window to draw in ScrollView::Color colour // colour to draw in ) const { inT16 stepindex; // index to cstep ICOORD pos; // current position DIR128 stepdir; // direction of step pos = start; // current position window->Pen(colour); if (stepcount == 0) { window->Rectangle(box.left(), box.top(), box.right(), box.bottom()); return; } window->SetCursor(pos.x(), pos.y()); stepindex = 0; while (stepindex < stepcount) { pos += step(stepindex); // step to next stepdir = step_dir(stepindex); stepindex++; // count steps // merge straight lines while (stepindex < stepcount && stepdir.get_dir() == step_dir(stepindex).get_dir()) { pos += step(stepindex); stepindex++; } window->DrawTo(pos.x(), pos.y()); } }
void vertical_coutline_projection( //project outlines C_OUTLINE *outline, //outline to project STATS *stats //output ) { ICOORD pos; //current point ICOORD step; //edge step inT32 length; //of outline inT16 stepindex; //current step C_OUTLINE_IT out_it = outline->child (); pos = outline->start_pos (); length = outline->pathlength (); for (stepindex = 0; stepindex < length; stepindex++) { step = outline->step (stepindex); if (step.x () > 0) { stats->add (pos.x (), -pos.y ()); } else if (step.x () < 0) { stats->add (pos.x () - 1, pos.y ()); } pos += step; } for (out_it.mark_cycle_pt (); !out_it.cycled_list (); out_it.forward ()) { vertical_coutline_projection (out_it.data (), stats); } }
void find_cblob_hlimits( //get x limits C_BLOB *blob, //blob to search float bottomy, //y limits float topy, float &xmin, //output x limits float &xmax) { INT16 stepindex; //current point ICOORD pos; //current coords ICOORD vec; //rotated step C_OUTLINE *outline; //current outline //outlines C_OUTLINE_IT out_it = blob->out_list (); xmin = (float) MAX_INT32; xmax = (float) -MAX_INT32; for (out_it.mark_cycle_pt (); !out_it.cycled_list (); out_it.forward ()) { outline = out_it.data (); pos = outline->start_pos (); //get coords for (stepindex = 0; stepindex < outline->pathlength (); stepindex++) { //inside if (pos.y () >= bottomy && pos.y () <= topy) { if (pos.x () > xmax) xmax = pos.x (); if (pos.x () < xmin) xmin = pos.x (); } vec = outline->step (stepindex); pos += vec; //move to next } } }
void find_cblob_vlimits( //get y limits C_BLOB *blob, //blob to search float leftx, //x limits float rightx, float &ymin, //output y limits float &ymax) { inT16 stepindex; //current point ICOORD pos; //current coords ICOORD vec; //rotated step C_OUTLINE *outline; //current outline //outlines C_OUTLINE_IT out_it = blob->out_list (); ymin = (float) MAX_INT32; ymax = (float) -MAX_INT32; for (out_it.mark_cycle_pt (); !out_it.cycled_list (); out_it.forward ()) { outline = out_it.data (); pos = outline->start_pos (); //get coords for (stepindex = 0; stepindex < outline->pathlength (); stepindex++) { //inside if (pos.x () >= leftx && pos.x () <= rightx) { UpdateRange(pos.y(), &ymin, &ymax); } vec = outline->step (stepindex); pos += vec; //move to next } } }
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); }
OL_BUCKETS::OL_BUCKETS( ICOORD bleft, // corners ICOORD tright): bl(bleft), tr(tright) { bxdim =(tright.x() - bleft.x()) / BUCKETSIZE + 1; bydim =(tright.y() - bleft.y()) / BUCKETSIZE + 1; // make array buckets = new C_OUTLINE_LIST[bxdim * bydim]; index = 0; }
// (Re)Initialize the grid. The gridsize is the size in pixels of each cell, // and bleft, tright are the bounding box of everything to go in it. void GridBase::Init(int gridsize, const ICOORD& bleft, const ICOORD& tright) { gridsize_ = gridsize; bleft_ = bleft; tright_ = tright; if (gridsize_ == 0) gridsize_ = 1; gridwidth_ = (tright.x() - bleft.x() + gridsize_ - 1) / gridsize_; gridheight_ = (tright.y() - bleft.y() + gridsize_ - 1) / gridsize_; gridbuckets_ = gridwidth_ * gridheight_; }
// Finds vertical lines in the given list of BLOBNBOXes. bleft and tright // are the bounds of the image on which the input line_bblobs were found. // The input line_bblobs list is const really. // The output vertical_x and vertical_y are the total of all the vectors. // The output list of TabVector makes no reference to the input BLOBNBOXes. void LineFinder::FindLineVectors(const ICOORD& bleft, const ICOORD& tright, BLOBNBOX_LIST* line_bblobs, int* vertical_x, int* vertical_y, TabVector_LIST* vectors) { BLOBNBOX_IT bbox_it(line_bblobs); int b_count = 0; // Put all the blobs into the grid to find the lines, and move the blobs // to the output lists. AlignedBlob blob_grid(kLineFindGridSize, bleft, tright); for (bbox_it.mark_cycle_pt(); !bbox_it.cycled_list(); bbox_it.forward()) { BLOBNBOX* bblob = bbox_it.data(); bblob->set_left_tab_type(TT_UNCONFIRMED); bblob->set_left_rule(bleft.x()); bblob->set_right_rule(tright.x()); bblob->set_left_crossing_rule(bleft.x()); bblob->set_right_crossing_rule(tright.x()); blob_grid.InsertBBox(false, true, bblob); ++b_count; } if (textord_debug_tabfind) tprintf("Inserted %d line blobs into grid\n", b_count); if (b_count == 0) return; // Search the entire grid, looking for vertical line vectors. GridSearch<BLOBNBOX, BLOBNBOX_CLIST, BLOBNBOX_C_IT> lsearch(&blob_grid); BLOBNBOX* bbox; TabVector_IT vector_it(vectors); *vertical_x = 0; *vertical_y = 1; lsearch.StartFullSearch(); while ((bbox = lsearch.NextFullSearch()) != NULL) { if (bbox->left_tab_type() == TT_UNCONFIRMED) { const TBOX& box = bbox->bounding_box(); if (AlignedBlob::WithinTestRegion(2, box.left(), box.bottom())) tprintf("Finding line vector starting at bbox (%d,%d)\n", box.left(), box.bottom()); AlignedBlobParams align_params(*vertical_x, *vertical_y, box.width()); TabVector* vector = blob_grid.FindVerticalAlignment(align_params, bbox, vertical_x, vertical_y); if (vector != NULL) { vector->Freeze(); vector_it.add_to_end(vector); } } } ScrollView* line_win = NULL; if (textord_tabfind_show_vlines) { line_win = blob_grid.MakeWindow(0, 50, "Vlines"); blob_grid.DisplayBoxes(line_win); line_win = blob_grid.DisplayTabs("Vlines", line_win); } }
/********************************************************************** * 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); }
// Renders the outline to the given pix, with left and top being // the coords of the upper-left corner of the pix. void C_OUTLINE::render(int left, int top, Pix* pix) const { ICOORD pos = start; for (int stepindex = 0; stepindex < stepcount; ++stepindex) { ICOORD next_step = step(stepindex); if (next_step.y() < 0) { pixRasterop(pix, 0, top - pos.y(), pos.x() - left, 1, PIX_NOT(PIX_DST), NULL, 0, 0); } else if (next_step.y() > 0) { pixRasterop(pix, 0, top - pos.y() - 1, pos.x() - left, 1, PIX_NOT(PIX_DST), NULL, 0, 0); } pos += next_step; } }
// As TraceOutlineOnReducedPix above, but on a BLOCK instead of a C_OUTLINE. Pix* TraceBlockOnReducedPix(BLOCK* block, int gridsize, ICOORD bleft, int* left, int* bottom) { TBOX box = block->bounding_box(); Pix* pix = GridReducedPix(box, gridsize, bleft, left, bottom); int wpl = pixGetWpl(pix); l_uint32* data = pixGetData(pix); ICOORDELT_IT it(block->poly_block()->points()); for (it.mark_cycle_pt(); !it.cycled_list();) { ICOORD pos = *it.data(); it.forward(); ICOORD next_pos = *it.data(); ICOORD line_vector = next_pos - pos; int major, minor; ICOORD major_step, minor_step; line_vector.setup_render(&major_step, &minor_step, &major, &minor); int accumulator = major / 2; while (pos != next_pos) { int grid_x = (pos.x() - bleft.x()) / gridsize - *left; int grid_y = (pos.y() - bleft.y()) / gridsize - *bottom; SET_DATA_BIT(data + grid_y * wpl, grid_x); pos += major_step; accumulator += minor; if (accumulator >= major) { accumulator -= major; pos += minor_step; } } } return pix; }
BOOL8 PDBLK::contains( //test containment ICOORD pt //point to test ) { BLOCK_RECT_IT it = this; //rectangle iterator ICOORD bleft, tright; //corners of rectangle for (it.start_block (); !it.cycled_rects (); it.forward ()) { //get rectangle it.bounding_box (bleft, tright); //inside rect if (pt.x () >= bleft.x () && pt.x () <= tright.x () && pt.y () >= bleft.y () && pt.y () <= tright.y ()) return TRUE; //is inside } return FALSE; //not inside }
inT16 POLY_BLOCK::winding_number(const ICOORD &point) { inT16 count; //winding count ICOORD pt; //current point ICOORD vec; //point to current point ICOORD vvec; //current point to next point inT32 cross; //cross product ICOORDELT_IT it = &vertices; //iterator count = 0; do { pt = *it.data (); vec = pt - point; vvec = *it.data_relative (1) - pt; //crossing the line if (vec.y () <= 0 && vec.y () + vvec.y () > 0) { cross = vec * vvec; //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 () + vvec.y () <= 0) { cross = vec * vvec; if (cross < 0) count--; //crossing back else if (cross == 0) return INTERSECTING; //illegal } else if (vec.y () == 0 && vec.x () == 0) return INTERSECTING; it.forward (); } while (!it.at_first ()); return count; //winding number }
// Renders just the outline to the given pix (no fill), with left and top // being the coords of the upper-left corner of the pix. void C_OUTLINE::render_outline(int left, int top, Pix* pix) const { ICOORD pos = start; for (int stepindex = 0; stepindex < stepcount; ++stepindex) { ICOORD next_step = step(stepindex); if (next_step.y() < 0) { pixSetPixel(pix, pos.x() - left, top - pos.y(), 1); } else if (next_step.y() > 0) { pixSetPixel(pix, pos.x() - left - 1, top - pos.y() - 1, 1); } else if (next_step.x() < 0) { pixSetPixel(pix, pos.x() - left - 1, top - pos.y(), 1); } else if (next_step.x() > 0) { pixSetPixel(pix, pos.x() - left, top - pos.y() - 1, 1); } pos += next_step; } }
// Helper function to compute a fictitious end point that is on a line // of a given gradient through the given start. ICOORD ComputeEndFromGradient(const ICOORD& start, double m) { if (m > 1.0 || m < -1.0) { // dy dominates. Force it to have the opposite sign of start.y() and // compute dx based on dy being as large as possible int dx = static_cast<int>(floor(MAX_INT16 / m)); if (dx < 0) ++dx; // Truncate towards 0. if (start.y() > 0) dx = - dx; // Force dy to be opposite to start.y(). // Constrain dx so the result fits in an inT16. while (start.x() + dx > MAX_INT16 || start.x() + dx < -MAX_INT16) dx /= 2; if (-1 <= dx && dx <= 1) { return ICOORD(start.x(), start.y() + 1); // Too steep for anything else. } int y = start.y() + static_cast<int>(floor(dx * m + 0.5)); ASSERT_HOST(-MAX_INT16 <= y && y <= MAX_INT16); return ICOORD(start.x() + dx, y); } else { // dx dominates. Force it to have the opposite sign of start.x() and // compute dy based on dx being as large as possible. int dy = static_cast<int>(floor(MAX_INT16 * m)); if (dy < 0) ++dy; // Truncate towards 0. if (start.x() > 0) dy = - dy; // Force dx to be opposite to start.x(). // Constrain dy so the result fits in an inT16. while (start.y() + dy > MAX_INT16 || start.y() + dy < -MAX_INT16) dy /= 2; if (-1 <= dy && dy <= 1) { return ICOORD(start.x() + 1, start.y()); // Too flat for anything else. } int x = start.x() + static_cast<int>(floor(dy / m + 0.5)); ASSERT_HOST(-MAX_INT16 <= x && x <= MAX_INT16); return ICOORD(x, start.y() + dy); } }
// Moves by the given vec in place. void TESSLINE::Move(const ICOORD vec) { EDGEPT* pt = loop; do { pt->pos.x += vec.x(); pt->pos.y += vec.y(); pt = pt->next; } while (pt != loop); SetupFromPos(); }
inT16 loop_bounding_box( //get bounding box CRACKEDGE *&start, //edge loop ICOORD &botleft, //bounding box ICOORD &topright) { inT16 length; //length of loop inT16 leftmost; //on top row CRACKEDGE *edgept; //current point CRACKEDGE *realstart; //topleft start edgept = start; realstart = start; botleft = topright = ICOORD (edgept->pos.x (), edgept->pos.y ()); leftmost = edgept->pos.x (); length = 0; //coutn length do { edgept = edgept->next; if (edgept->pos.x () < botleft.x ()) //get bounding box botleft.set_x (edgept->pos.x ()); else if (edgept->pos.x () > topright.x ()) topright.set_x (edgept->pos.x ()); if (edgept->pos.y () < botleft.y ()) //get bounding box botleft.set_y (edgept->pos.y ()); else if (edgept->pos.y () > topright.y ()) { realstart = edgept; leftmost = edgept->pos.x (); topright.set_y (edgept->pos.y ()); } else if (edgept->pos.y () == topright.y () && edgept->pos.x () < leftmost) { //leftmost on line leftmost = edgept->pos.x (); realstart = edgept; } length++; //count elements } while (edgept != start); start = realstart; //shift it to topleft return length; }
// Adds sub-pixel resolution EdgeOffsets for the outline using only // a binary image source. // Runs a sliding window of 5 edge steps over the outline, maintaining a count // of the number of steps in each of the 4 directions in the window, and a // sum of the x or y position of each step (as appropriate to its direction.) // Ignores single-count steps EXCEPT the sharp U-turn and smoothes out the // perpendicular direction. Eg // ___ ___ Chain code from the left: // |___ ___ ___| 222122212223221232223000 // |___| |_| Corresponding counts of each direction: // 0 00000000000000000123 // 1 11121111001111100000 // 2 44434443443333343321 // 3 00000001111111112111 // Count of direction at center 41434143413313143313 // Step gets used? YNYYYNYYYNYYNYNYYYyY (y= U-turn exception) // Path redrawn showing only the used points: // ___ ___ // ___ ___ ___| // ___ _ // Sub-pixel edge position cannot be shown well with ASCII-art, but each // horizontal step's y position is the mean of the y positions of the steps // in the same direction in the sliding window, which makes a much smoother // outline, without losing important detail. void C_OUTLINE::ComputeBinaryOffsets() { delete [] offsets; offsets = new EdgeOffset[stepcount]; // Count of the number of steps in each direction in the sliding window. int dir_counts[4]; // Sum of the positions (y for a horizontal step, x for vertical) in each // direction in the sliding window. int pos_totals[4]; memset(dir_counts, 0, sizeof(dir_counts)); memset(pos_totals, 0, sizeof(pos_totals)); ICOORD pos = start; ICOORD tail_pos = pos; // tail_pos is the trailing position, with the next point to be lost from // the window. tail_pos -= step(stepcount - 1); tail_pos -= step(stepcount - 2); // head_pos is the leading position, with the next point to be added to the // window. ICOORD head_pos = tail_pos; // Set up the initial window with 4 points in [-2, 2) for (int s = -2; s < 2; ++s) { increment_step(s, 1, &head_pos, dir_counts, pos_totals); } for (int s = 0; s < stepcount; pos += step(s++)) { // At step s, s in in the middle of [s-2, s+2]. increment_step(s + 2, 1, &head_pos, dir_counts, pos_totals); int dir_index = chain_code(s); ICOORD step_vec = step(s); int best_diff = 0; int offset = 0; // Use only steps that have a count of >=2 OR the strong U-turn with a // single d and 2 at d-1 and 2 at d+1 (mod 4). if (dir_counts[dir_index] >= 2 || (dir_counts[dir_index] == 1 && dir_counts[Modulo(dir_index - 1, 4)] == 2 && dir_counts[Modulo(dir_index + 1, 4)] == 2)) { // Valid step direction. best_diff = dir_counts[dir_index]; int edge_pos = step_vec.x() == 0 ? pos.x() : pos.y(); // The offset proposes that the actual step should be positioned at // the mean position of the steps in the window of the same direction. // See ASCII art above. offset = pos_totals[dir_index] - best_diff * edge_pos; } offsets[s].offset_numerator = static_cast<inT8>(ClipToRange(offset, -MAX_INT8, MAX_INT8)); offsets[s].pixel_diff = static_cast<uinT8>(ClipToRange(best_diff, 0 , MAX_UINT8)); // The direction is just the vector from start to end of the window. FCOORD direction(head_pos.x() - tail_pos.x(), head_pos.y() - tail_pos.y()); offsets[s].direction = direction.to_direction(); increment_step(s - 2, -1, &tail_pos, dir_counts, pos_totals); } }
void QSPLINE::move( // reposition spline ICOORD vec // by vector ) { inT32 segment; //index of segment inT16 x_shift = vec.x (); for (segment = 0; segment < segments; segment++) { xcoords[segment] += x_shift; quadratics[segment].move (vec); } xcoords[segment] += x_shift; }
TBOX::TBOX( //construtor const ICOORD pt1, //one corner const ICOORD pt2 //the other corner ) { if (pt1.x () <= pt2.x ()) { if (pt1.y () <= pt2.y ()) { bot_left = pt1; top_right = pt2; } else { bot_left = ICOORD (pt1.x (), pt2.y ()); top_right = ICOORD (pt2.x (), pt1.y ()); } } else { if (pt1.y () <= pt2.y ()) { bot_left = ICOORD (pt2.x (), pt1.y ()); top_right = ICOORD (pt1.x (), pt2.y ()); } else { bot_left = pt2; top_right = pt1; } } }
inT16 BLOCK_LINE_IT::get_line( //get a line inT16 y, //line to get inT16 &xext //output extent ) { ICOORD bleft; //bounding box ICOORD tright; //of block & rect //get block box block->bounding_box (bleft, tright); if (y < bleft.y () || y >= tright.y ()) { // block->print(stderr,FALSE); BADBLOCKLINE.error ("BLOCK_LINE_IT::get_line", ABORT, "Y=%d", y); } //get rectangle box rect_it.bounding_box (bleft, tright); //inside rectangle if (y >= bleft.y () && y < tright.y ()) { //width of line xext = tright.x () - bleft.x (); return bleft.x (); //start of line } for (rect_it.start_block (); !rect_it.cycled_rects (); rect_it.forward ()) { //get rectangle box rect_it.bounding_box (bleft, tright); //inside rectangle if (y >= bleft.y () && y < tright.y ()) { //width of line xext = tright.x () - bleft.x (); return bleft.x (); //start of line } } LOSTBLOCKLINE.error ("BLOCK_LINE_IT::get_line", ABORT, "Y=%d", y); return 0; //dummy to stop warning }
// Helper function to return a scaled Pix with one pixel per grid cell, // set (black) where the given outline enters the corresponding grid cell, // and clear where the outline does not touch the grid cell. // Also returns the grid coords of the bottom-left of the Pix, in *left // and *bottom, which corresponds to (0, 0) on the Pix. // Note that the Pix is used upside-down, with (0, 0) being the bottom-left. Pix* TraceOutlineOnReducedPix(C_OUTLINE* outline, int gridsize, ICOORD bleft, int* left, int* bottom) { TBOX box = outline->bounding_box(); Pix* pix = GridReducedPix(box, gridsize, bleft, left, bottom); int wpl = pixGetWpl(pix); l_uint32* data = pixGetData(pix); int length = outline->pathlength(); ICOORD pos = outline->start_pos(); for (int i = 0; i < length; ++i) { int grid_x = (pos.x() - bleft.x()) / gridsize - *left; int grid_y = (pos.y() - bleft.y()) / gridsize - *bottom; SET_DATA_BIT(data + grid_y * wpl, grid_x); pos += outline->step(i); } return pix; }
/** Returns the polygon outline of the current block. The returned Pta must * be ptaDestroy-ed after use. */ Pta* PageIterator::BlockPolygon() const { if (it_->block() == NULL || it_->block()->block == NULL) return NULL; // Already at the end! if (it_->block()->block->poly_block() == NULL) return NULL; // No layout analysis used - no polygon. ICOORDELT_IT it(it_->block()->block->poly_block()->points()); Pta* pta = ptaCreate(it.length()); int num_pts = 0; for (it.mark_cycle_pt(); !it.cycled_list(); it.forward(), ++num_pts) { ICOORD* pt = it.data(); // Convert to top-down coords within the input image. float x = static_cast<float>(pt->x()) / scale_ + rect_left_; float y = rect_top_ + rect_height_ - static_cast<float>(pt->y()) / scale_; ptaAddPt(pta, x, y); } return pta; }
void PDBLK::plot( //draw outline ScrollView* window, //window to draw in inT32 serial, //serial number ScrollView::Color colour //colour to draw in ) { ICOORD startpt; //start of outline ICOORD endpt; //end of outline ICOORD prevpt; //previous point ICOORDELT_IT it = &leftside; //iterator //set the colour window->Pen(colour); window->TextAttributes("Times", BLOCK_LABEL_HEIGHT, false, false, false); if (hand_poly != NULL) { hand_poly->plot(window, serial); } else if (!leftside.empty ()) { startpt = *(it.data ()); //bottom left corner // tprintf("Block %d bottom left is (%d,%d)\n", // serial,startpt.x(),startpt.y()); char temp_buff[34]; #ifdef __UNIX__ sprintf(temp_buff, INT32FORMAT, serial); #else ultoa (serial, temp_buff, 10); #endif window->Text(startpt.x (), startpt.y (), temp_buff); window->SetCursor(startpt.x (), startpt.y ()); do { prevpt = *(it.data ()); //previous point it.forward (); //move to next point //draw round corner window->DrawTo(prevpt.x (), it.data ()->y ()); window->DrawTo(it.data ()->x (), it.data ()->y ()); } while (!it.at_last ()); //until end of list endpt = *(it.data ()); //end point //other side of boundary window->SetCursor(startpt.x (), startpt.y ()); it.set_to_list (&rightside); prevpt = startpt; for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ()) { //draw round corner window->DrawTo(prevpt.x (), it.data ()->y ()); window->DrawTo(it.data ()->x (), it.data ()->y ()); prevpt = *(it.data ()); //previous point } //close boundary window->DrawTo(endpt.x(), endpt.y()); } }
// Backwards compatible constrained fit with a supplied gradient. double DetLineFit::ConstrainedFit(double m, float* c) { ICOORDELT_IT it(&pt_list_); // Do something sensible with no points. if (pt_list_.empty()) { *c = 0.0f; return 0.0; } // Count the points and find the first and last kNumEndPoints. // Put the ends in a single array to make their use easier later. ICOORD* pts[kNumEndPoints * 2]; int pt_count = 0; for (it.mark_cycle_pt(); !it.cycled_list(); it.forward()) { if (pt_count < kNumEndPoints) { pts[pt_count] = it.data(); pts[kNumEndPoints + pt_count] = pts[pt_count]; } else { for (int i = 1; i < kNumEndPoints; ++i) pts[kNumEndPoints + i - 1] = pts[kNumEndPoints + i]; pts[kNumEndPoints * 2 - 1] = it.data(); } ++pt_count; } while (pt_count < kNumEndPoints) { pts[pt_count] = NULL; pts[kNumEndPoints + pt_count++] = NULL; } int* distances = new int[pt_count]; double best_uq = -1.0; // Iterate each pair of points and find the best fitting line. for (int i = 0; i < kNumEndPoints * 2; ++i) { ICOORD* start = pts[i]; if (start == NULL) continue; ICOORD end = ComputeEndFromGradient(*start, m); // Compute the upper quartile error from the line. double dist = ComputeErrors(*start, end, distances); if (dist < best_uq || best_uq < 0.0) { best_uq = dist; *c = start->y() - start->x() * m; } } delete [] distances; // Finally compute the square root to return the true distance. return best_uq > 0.0 ? sqrt(best_uq) : best_uq; }
void block_edges(Pix *t_pix, // thresholded image PDBLK *block, // block in image C_OUTLINE_IT* outline_it) { ICOORD bleft; // bounding box ICOORD tright; BLOCK_LINE_IT line_it = block; // line iterator int width = pixGetWidth(t_pix); int height = pixGetHeight(t_pix); int wpl = pixGetWpl(t_pix); // lines in progress CRACKEDGE **ptrline = new CRACKEDGE*[width + 1]; CRACKEDGE *free_cracks = NULL; block->bounding_box(bleft, tright); // block box int block_width = tright.x() - bleft.x(); for (int x = block_width; x >= 0; x--) ptrline[x] = NULL; // no lines in progress uinT8* bwline = new uinT8[width]; uinT8 margin = WHITE_PIX; for (int y = tright.y() - 1; y >= bleft.y() - 1; y--) { if (y >= bleft.y() && y < tright.y()) { // Get the binary pixels from the image. l_uint32* line = pixGetData(t_pix) + wpl * (height - 1 - y); for (int x = 0; x < block_width; ++x) { bwline[x] = GET_DATA_BIT(line, x + bleft.x()) ^ 1; } make_margins(block, &line_it, bwline, margin, bleft.x(), tright.x(), y); } else { memset(bwline, margin, block_width * sizeof(bwline[0])); } line_edges(bleft.x(), y, block_width, margin, bwline, ptrline, &free_cracks, outline_it); } free_crackedges(free_cracks); // really free them delete[] ptrline; delete[] bwline; }
bool fixed_chop_coutline( //chop the outline C_OUTLINE* srcline, //source outline int16_t chop_coord, //place to chop float pitch_error, //allowed deviation C_OUTLINE_FRAG_LIST* left_frags, //left half of chop C_OUTLINE_FRAG_LIST* right_frags //right half of chop ) { bool first_frag; //fragment int16_t left_edge; //of outline int16_t startindex; //in first fragment int32_t length; //of outline int16_t stepindex; //into outline int16_t head_index; //start of fragment ICOORD head_pos; //start of fragment int16_t tail_index; //end of fragment ICOORD tail_pos; //end of fragment ICOORD pos; //current point int16_t first_index = 0; //first tail ICOORD first_pos; //first tail length = srcline->pathlength (); pos = srcline->start_pos (); left_edge = pos.x (); tail_index = 0; tail_pos = pos; for (stepindex = 0; stepindex < length; stepindex++) { if (pos.x () < left_edge) { left_edge = pos.x (); tail_index = stepindex; tail_pos = pos; } pos += srcline->step (stepindex); } if (left_edge >= chop_coord - pitch_error) return false; //not worth it startindex = tail_index; first_frag = true; head_index = tail_index; head_pos = tail_pos; do { do { tail_pos += srcline->step (tail_index); tail_index++; if (tail_index == length) tail_index = 0; } while (tail_pos.x () != chop_coord && tail_index != startindex); if (tail_index == startindex) { if (first_frag) return false; //doesn't cross line else break; } //#ifdef __UNIX__ ASSERT_HOST (head_index != tail_index); //#endif if (!first_frag) { save_chop_cfragment(head_index, head_pos, tail_index, tail_pos, srcline, left_frags); } else { first_index = tail_index; first_pos = tail_pos; first_frag = false; } while (srcline->step (tail_index).x () == 0) { tail_pos += srcline->step (tail_index); tail_index++; if (tail_index == length) tail_index = 0; } head_index = tail_index; head_pos = tail_pos; while (srcline->step (tail_index).x () > 0) { do { tail_pos += srcline->step (tail_index); tail_index++; if (tail_index == length) tail_index = 0; } while (tail_pos.x () != chop_coord); //#ifdef __UNIX__ ASSERT_HOST (head_index != tail_index); //#endif save_chop_cfragment(head_index, head_pos, tail_index, tail_pos, srcline, right_frags); while (srcline->step (tail_index).x () == 0) { tail_pos += srcline->step (tail_index); tail_index++; if (tail_index == length) tail_index = 0; } head_index = tail_index; head_pos = tail_pos; } } while (tail_index != startindex); save_chop_cfragment(head_index, head_pos, first_index, first_pos, srcline, left_frags); return true; //did some chopping }
// (Re)Fit a line to the stored points. Returns false if the line // is degenerate. Althougth the TabVector code mostly doesn't care about the // direction of lines, XAtY would give silly results for a horizontal line. // The class is mostly aimed at use for vertical lines representing // horizontal tab stops. bool TabVector::Fit(ICOORD vertical, bool force_parallel) { needs_refit_ = false; if (boxes_.empty()) { // Don't refit something with no boxes, as that only happens // in Evaluate, and we don't want to end up with a zero vector. if (!force_parallel) return false; // If we are forcing parallel, then we just need to set the sort_key_. ICOORD midpt = startpt_; midpt += endpt_; midpt /= 2; sort_key_ = SortKey(vertical, midpt.x(), midpt.y()); return startpt_.y() != endpt_.y(); } if (!force_parallel && !IsRagged()) { // Use a fitted line as the vertical. DetLineFit linepoints; BLOBNBOX_C_IT it(&boxes_); // Fit a line to all the boxes in the list. for (it.mark_cycle_pt(); !it.cycled_list(); it.forward()) { BLOBNBOX* bbox = it.data(); TBOX box = bbox->bounding_box(); int x1 = IsRightTab() ? box.right() : box.left(); ICOORD boxpt(x1, box.bottom()); linepoints.Add(boxpt); if (it.at_last()) { ICOORD top_pt(x1, box.top()); linepoints.Add(top_pt); } } linepoints.Fit(&startpt_, &endpt_); if (startpt_.y() != endpt_.y()) { vertical = endpt_; vertical -= startpt_; } } int start_y = startpt_.y(); int end_y = endpt_.y(); sort_key_ = IsLeftTab() ? MAX_INT32 : -MAX_INT32; BLOBNBOX_C_IT it(&boxes_); // Choose a line parallel to the vertical such that all boxes are on the // correct side of it. mean_width_ = 0; int width_count = 0; for (it.mark_cycle_pt(); !it.cycled_list(); it.forward()) { BLOBNBOX* bbox = it.data(); TBOX box = bbox->bounding_box(); mean_width_ += box.width(); ++width_count; int x1 = IsRightTab() ? box.right() : box.left(); // Test both the bottom and the top, as one will be more extreme, depending // on the direction of skew. int bottom_y = box.bottom(); int top_y = box.top(); int key = SortKey(vertical, x1, bottom_y); if (IsLeftTab() == (key < sort_key_)) { sort_key_ = key; startpt_ = ICOORD(x1, bottom_y); } key = SortKey(vertical, x1, top_y); if (IsLeftTab() == (key < sort_key_)) { sort_key_ = key; startpt_ = ICOORD(x1, top_y); } if (it.at_first()) start_y = bottom_y; if (it.at_last()) end_y = top_y; } if (width_count > 0) { mean_width_ = (mean_width_ + width_count - 1) / width_count; } endpt_ = startpt_ + vertical; needs_evaluation_ = true; if (start_y != end_y) { // Set the ends of the vector to fully include the first and last blobs. startpt_.set_x(XAtY(vertical, sort_key_, start_y)); startpt_.set_y(start_y); endpt_.set_x(XAtY(vertical, sort_key_, end_y)); endpt_.set_y(end_y); return true; } return false; }
void block_edges(IMAGE *t_image, // thresholded image PDBLK *block, // block in image C_OUTLINE_IT* outline_it) { uinT8 margin; // margin colour inT16 x; // line coords inT16 y; // current line ICOORD bleft; // bounding box ICOORD tright; ICOORD block_bleft; // bounding box ICOORD block_tright; int xindex; // index to pixel BLOCK_LINE_IT line_it = block; // line iterator IMAGELINE bwline; // thresholded line // lines in progress CRACKEDGE **ptrline = new CRACKEDGE*[t_image->get_xsize()+1]; CRACKEDGE *free_cracks = NULL; block->bounding_box(bleft, tright); // block box block_bleft = bleft; block_tright = tright; for (x = tright.x() - bleft.x(); x >= 0; x--) ptrline[x] = NULL; //no lines in progress bwline.init(t_image->get_xsize()); margin = WHITE_PIX; for (y = tright.y() - 1; y >= bleft.y() - 1; y--) { if (y >= block_bleft.y() && y < block_tright.y()) { t_image->get_line(bleft.x(), y, tright.x() - bleft.x(), &bwline, 0); make_margins(block, &line_it, bwline.pixels, margin, bleft.x(), tright.x(), y); } else { x = tright.x() - bleft.x(); for (xindex = 0; xindex < x; xindex++) bwline.pixels[xindex] = margin; } line_edges(bleft.x(), y, tright.x() - bleft.x(), margin, bwline.pixels, ptrline, &free_cracks, outline_it); } free_crackedges(free_cracks); // really free them delete[] ptrline; }