// 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; } }
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; }
inT16 POLY_BLOCK::winding_number( //winding number const ICOORD &point //point to wind around ) { 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 }
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; }
inT32 C_OUTLINE::outer_area() const { int stepindex; //current step inT32 total_steps; //steps to do inT32 total; //total area ICOORD pos; //position of point ICOORD next_step; //step to next pix pos = start_pos (); total_steps = pathlength (); if (total_steps == 0) return box.area(); total = 0; for (stepindex = 0; stepindex < total_steps; stepindex++) { //all intersected next_step = step (stepindex); if (next_step.x () < 0) total += pos.y (); else if (next_step.x () > 0) total -= pos.y (); pos += next_step; } return total; }
// Return true if this vector is the same side, overlaps, and close // enough to the other to be merged. bool TabVector::SimilarTo(const ICOORD& vertical, const TabVector& other, BlobGrid* grid) const { if ((IsRightTab() && other.IsRightTab()) || (IsLeftTab() && other.IsLeftTab())) { // If they don't overlap, at least in extensions, then there is no chance. if (ExtendedOverlap(other.extended_ymax_, other.extended_ymin_) < 0) return false; // A fast approximation to the scale factor of the sort_key_. int v_scale = abs(vertical.y()); if (v_scale == 0) v_scale = 1; // If they are close enough, then OK. if (sort_key_ + kSimilarVectorDist * v_scale >= other.sort_key_ && sort_key_ - kSimilarVectorDist * v_scale <= other.sort_key_) return true; // Ragged tabs get a bigger threshold. if (!IsRagged() || !other.IsRagged() || sort_key_ + kSimilarRaggedDist * v_scale < other.sort_key_ || sort_key_ - kSimilarRaggedDist * v_scale > other.sort_key_) return false; if (grid == NULL) { // There is nothing else to test! return true; } // If there is nothing in the rectangle between the vector that is going to // move, and the place it is moving to, then they can be merged. // Setup a vertical search for any blob. const TabVector* mover = (IsRightTab() && sort_key_ < other.sort_key_) ? this : &other; int top_y = mover->endpt_.y(); int bottom_y = mover->startpt_.y(); int left = MIN(mover->XAtY(top_y), mover->XAtY(bottom_y)); int right = MAX(mover->XAtY(top_y), mover->XAtY(bottom_y)); int shift = abs(sort_key_ - other.sort_key_) / v_scale; if (IsRightTab()) { right += shift; } else { left -= shift; } GridSearch<BLOBNBOX, BLOBNBOX_CLIST, BLOBNBOX_C_IT> vsearch(grid); vsearch.StartVerticalSearch(left, right, top_y); BLOBNBOX* blob; while ((blob = vsearch.NextVerticalSearch(true)) != NULL) { TBOX box = blob->bounding_box(); if (box.top() > bottom_y) return true; // Nothing found. if (box.bottom() < top_y) continue; // Doesn't overlap. int left_at_box = XAtY(box.bottom()); int right_at_box = left_at_box; if (IsRightTab()) right_at_box += shift; else left_at_box -= shift; if (MIN(right_at_box, box.right()) > MAX(left_at_box, box.left())) return false; } return true; // Nothing found. } 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; }
void vertical_cunderline_projection( //project outlines C_OUTLINE *outline, //outline to project QSPLINE *baseline, //actual baseline float xheight, //height of line float baseline_offset, //amount to shrinke it STATS *lower_proj, //below baseline STATS *middle_proj, //centre region STATS *upper_proj //top region ) { ICOORD pos; //current point ICOORD step; //edge step int16_t lower_y, upper_y; //region limits int32_t length; //of outline int16_t 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) { lower_y = (int16_t) floor (baseline->y (pos.x ()) + baseline_offset + 0.5); upper_y = (int16_t) floor (baseline->y (pos.x ()) + baseline_offset + xheight + 0.5); if (pos.y () >= lower_y) { lower_proj->add (pos.x (), -lower_y); if (pos.y () >= upper_y) { middle_proj->add (pos.x (), lower_y - upper_y); upper_proj->add (pos.x (), upper_y - pos.y ()); } else middle_proj->add (pos.x (), lower_y - pos.y ()); } else lower_proj->add (pos.x (), -pos.y ()); } else if (step.x () < 0) { lower_y = (int16_t) floor (baseline->y (pos.x () - 1) + baseline_offset + 0.5); upper_y = (int16_t) floor (baseline->y (pos.x () - 1) + baseline_offset + xheight + 0.5); if (pos.y () >= lower_y) { lower_proj->add (pos.x () - 1, lower_y); if (pos.y () >= upper_y) { middle_proj->add (pos.x () - 1, upper_y - lower_y); upper_proj->add (pos.x () - 1, pos.y () - upper_y); } else middle_proj->add (pos.x () - 1, pos.y () - lower_y); } else lower_proj->add (pos.x () - 1, pos.y ()); } pos += step; } for (out_it.mark_cycle_pt (); !out_it.cycled_list (); out_it.forward ()) { vertical_cunderline_projection (out_it.data (), baseline, xheight, baseline_offset, lower_proj, middle_proj, upper_proj); } }
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; } } }
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()); } }
/** * 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 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); }
EDGEPT * edgesteps_to_edgepts ( //convert outline C_OUTLINE * c_outline, //input EDGEPT edgepts[] //output is array ) { inT32 length; //steps in path ICOORD pos; //current coords inT32 stepindex; //current step inT32 stepinc; //increment inT32 epindex; //current EDGEPT inT32 count; //repeated steps ICOORD vec; //for this 8 step ICOORD prev_vec; inT8 epdir; //of this step DIR128 prevdir; //prvious dir DIR128 dir; //of this step pos = c_outline->start_pos (); //start of loop length = c_outline->pathlength (); stepindex = 0; epindex = 0; prevdir = -1; count = 0; int prev_stepindex = 0; do { dir = c_outline->step_dir (stepindex); vec = c_outline->step (stepindex); if (stepindex < length - 1 && c_outline->step_dir (stepindex + 1) - dir == -32) { dir += 128 - 16; vec += c_outline->step (stepindex + 1); stepinc = 2; } else stepinc = 1; if (count == 0) { prevdir = dir; prev_vec = vec; } if (prevdir.get_dir () != dir.get_dir ()) { edgepts[epindex].pos.x = pos.x (); edgepts[epindex].pos.y = pos.y (); prev_vec *= count; edgepts[epindex].vec.x = prev_vec.x (); edgepts[epindex].vec.y = prev_vec.y (); pos += prev_vec; edgepts[epindex].flags[RUNLENGTH] = count; edgepts[epindex].prev = &edgepts[epindex - 1]; edgepts[epindex].flags[FLAGS] = 0; edgepts[epindex].next = &edgepts[epindex + 1]; prevdir += 64; epdir = (DIR128) 0 - prevdir; epdir >>= 4; epdir &= 7; edgepts[epindex].flags[DIR] = epdir; edgepts[epindex].src_outline = c_outline; edgepts[epindex].start_step = prev_stepindex; edgepts[epindex].step_count = stepindex - prev_stepindex; epindex++; prevdir = dir; prev_vec = vec; count = 1; prev_stepindex = stepindex; } else count++; stepindex += stepinc; }
// 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); } }
// (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(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; }
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 ()); }
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 }
inT32 C_OUTLINE::count_transitions( //winding number inT32 threshold //on size ) { BOOL8 first_was_max_x; //what was first BOOL8 first_was_max_y; BOOL8 looking_for_max_x; //what is next BOOL8 looking_for_min_x; BOOL8 looking_for_max_y; //what is next BOOL8 looking_for_min_y; int stepindex; //current step inT32 total_steps; //steps to do //current limits inT32 max_x, min_x, max_y, min_y; inT32 initial_x, initial_y; //initial limits inT32 total; //total changes ICOORD pos; //position of point ICOORD next_step; //step to next pix pos = start_pos (); total_steps = pathlength (); total = 0; max_x = min_x = pos.x (); max_y = min_y = pos.y (); looking_for_max_x = TRUE; looking_for_min_x = TRUE; looking_for_max_y = TRUE; looking_for_min_y = TRUE; first_was_max_x = FALSE; first_was_max_y = FALSE; initial_x = pos.x (); initial_y = pos.y (); //stop uninit warning for (stepindex = 0; stepindex < total_steps; stepindex++) { //all intersected next_step = step (stepindex); pos += next_step; if (next_step.x () < 0) { if (looking_for_max_x && pos.x () < min_x) min_x = pos.x (); if (looking_for_min_x && max_x - pos.x () > threshold) { if (looking_for_max_x) { initial_x = max_x; first_was_max_x = FALSE; } total++; looking_for_max_x = TRUE; looking_for_min_x = FALSE; min_x = pos.x (); //reset min } } else if (next_step.x () > 0) { if (looking_for_min_x && pos.x () > max_x) max_x = pos.x (); if (looking_for_max_x && pos.x () - min_x > threshold) { if (looking_for_min_x) { initial_x = min_x; //remember first min first_was_max_x = TRUE; } total++; looking_for_max_x = FALSE; looking_for_min_x = TRUE; max_x = pos.x (); } } else if (next_step.y () < 0) { if (looking_for_max_y && pos.y () < min_y) min_y = pos.y (); if (looking_for_min_y && max_y - pos.y () > threshold) { if (looking_for_max_y) { initial_y = max_y; //remember first max first_was_max_y = FALSE; } total++; looking_for_max_y = TRUE; looking_for_min_y = FALSE; min_y = pos.y (); //reset min } } else { if (looking_for_min_y && pos.y () > max_y) max_y = pos.y (); if (looking_for_max_y && pos.y () - min_y > threshold) { if (looking_for_min_y) { initial_y = min_y; //remember first min first_was_max_y = TRUE; } total++; looking_for_max_y = FALSE; looking_for_min_y = TRUE; max_y = pos.y (); } } } if (first_was_max_x && looking_for_min_x) { if (max_x - initial_x > threshold) total++; else total--; } else if (!first_was_max_x && looking_for_max_x) { if (initial_x - min_x > threshold) total++; else total--; } if (first_was_max_y && looking_for_min_y) { if (max_y - initial_y > threshold) total++; else total--; } else if (!first_was_max_y && looking_for_max_y) { if (initial_y - min_y > threshold) total++; else total--; } return total; }
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 }
// Adds sub-pixel resolution EdgeOffsets for the outline if the supplied // pix is 8-bit. Does nothing otherwise. // Operation: Consider the following near-horizontal line: // _________ // |________ // |________ // At *every* position along this line, the gradient direction will be close // to vertical. Extrapoaltion/interpolation of the position of the threshold // that was used to binarize the image gives a more precise vertical position // for each horizontal step, and the conflict in step direction and gradient // direction can be used to ignore the vertical steps. void C_OUTLINE::ComputeEdgeOffsets(int threshold, Pix* pix) { if (pixGetDepth(pix) != 8) return; const l_uint32* data = pixGetData(pix); int wpl = pixGetWpl(pix); int width = pixGetWidth(pix); int height = pixGetHeight(pix); bool negative = flag(COUT_INVERSE); delete [] offsets; offsets = new EdgeOffset[stepcount]; ICOORD pos = start; ICOORD prev_gradient; ComputeGradient(data, wpl, pos.x(), height - pos.y(), width, height, &prev_gradient); for (int s = 0; s < stepcount; ++s) { ICOORD step_vec = step(s); TPOINT pt1(pos); pos += step_vec; TPOINT pt2(pos); ICOORD next_gradient; ComputeGradient(data, wpl, pos.x(), height - pos.y(), width, height, &next_gradient); // Use the sum of the prev and next as the working gradient. ICOORD gradient = prev_gradient + next_gradient; // best_diff will be manipulated to be always positive. int best_diff = 0; // offset will be the extrapolation of the location of the greyscale // threshold from the edge with the largest difference, relative to the // location of the binary edge. int offset = 0; if (pt1.y == pt2.y && abs(gradient.y()) * 2 >= abs(gradient.x())) { // Horizontal step. diff_sign == 1 indicates black above. int diff_sign = (pt1.x > pt2.x) == negative ? 1 : -1; int x = MIN(pt1.x, pt2.x); int y = height - pt1.y; int best_sum = 0; int best_y = y; EvaluateVerticalDiff(data, wpl, diff_sign, x, y, height, &best_diff, &best_sum, &best_y); // Find the strongest edge. int test_y = y; do { ++test_y; } while (EvaluateVerticalDiff(data, wpl, diff_sign, x, test_y, height, &best_diff, &best_sum, &best_y)); test_y = y; do { --test_y; } while (EvaluateVerticalDiff(data, wpl, diff_sign, x, test_y, height, &best_diff, &best_sum, &best_y)); offset = diff_sign * (best_sum / 2 - threshold) + (y - best_y) * best_diff; } else if (pt1.x == pt2.x && abs(gradient.x()) * 2 >= abs(gradient.y())) { // Vertical step. diff_sign == 1 indicates black on the left. int diff_sign = (pt1.y > pt2.y) == negative ? 1 : -1; int x = pt1.x; int y = height - MAX(pt1.y, pt2.y); const l_uint32* line = pixGetData(pix) + y * wpl; int best_sum = 0; int best_x = x; EvaluateHorizontalDiff(line, diff_sign, x, width, &best_diff, &best_sum, &best_x); // Find the strongest edge. int test_x = x; do { ++test_x; } while (EvaluateHorizontalDiff(line, diff_sign, test_x, width, &best_diff, &best_sum, &best_x)); test_x = x; do { --test_x; } while (EvaluateHorizontalDiff(line, diff_sign, test_x, width, &best_diff, &best_sum, &best_x)); offset = diff_sign * (threshold - best_sum / 2) + (best_x - x) * best_diff; } 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)); if (negative) gradient = -gradient; // Compute gradient angle quantized to 256 directions, rotated by 64 (pi/2) // to convert from gradient direction to edge direction. offsets[s].direction = Modulo(FCOORD::binary_angle_plus_pi(gradient.angle()) + 64, 256); prev_gradient = next_gradient; } }
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); }
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) { if (pitsync_projection_fix) stats->add (pos.x (), -pos.y ()); else stats->add (pos.x (), pos.y ()); } else if (step.x () < 0) { if (pitsync_projection_fix) stats->add (pos.x () - 1, pos.y ()); else 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); } }