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) { UpdateRange(pos.x(), &xmin, &xmax); } 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) { if (pos.y () > ymax) ymax = pos.y (); if (pos.y () < ymin) ymin = pos.y (); } vec = outline->step (stepindex); pos += vec; //move to next } } }
inT32 C_OUTLINE::area() { //winding number 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 C_OUTLINE_IT it = child (); pos = start_pos (); total_steps = pathlength (); 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; } for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ()) total += it.data ()->area ();//add areas of children return total; }
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); }
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 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 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); } }
inT32 C_OUTLINE::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 // We aren't going to modify the list, or its contents, but there is // no const iterator. C_OUTLINE_IT it(const_cast<C_OUTLINE_LIST*>(&children)); pos = start_pos (); total_steps = pathlength (); 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; } for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ()) total += it.data ()->area ();//add areas of children return total; }
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_; }
/********************************************************************** * 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); }
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()); } }
// 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; } }
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 find_cblob_limits( //get y limits C_BLOB *blob, //blob to search float leftx, //x limits float rightx, FCOORD rotation, //for landscape float &ymin, //output y limits float &ymax) { int16_t 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) INT32_MAX; ymax = (float) -INT32_MAX; for (out_it.mark_cycle_pt (); !out_it.cycled_list (); out_it.forward ()) { outline = out_it.data (); pos = outline->start_pos (); //get coords pos.rotate (rotation); for (stepindex = 0; stepindex < outline->pathlength (); stepindex++) { //inside if (pos.x () >= leftx && pos.x () <= rightx) { UpdateRange(pos.y(), &ymin, &ymax); } vec = outline->step (stepindex); vec.rotate (rotation); pos += vec; //move to next } } }
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 }
// 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; }
C_OUTLINE_FRAG::C_OUTLINE_FRAG( //record fragment ICOORD start_pt, //start coord ICOORD end_pt, //end coord C_OUTLINE *outline, //source of steps int16_t start_index, int16_t end_index) { start = start_pt; end = end_pt; ycoord = start_pt.y (); stepcount = end_index - start_index; if (stepcount < 0) stepcount += outline->pathlength (); ASSERT_HOST (stepcount > 0); steps = new DIR128[stepcount]; if (end_index > start_index) { for (int i = start_index; i < end_index; ++i) steps[i - start_index] = outline->step_dir(i); } else { int len = outline->pathlength(); int i = start_index; for (; i < len; ++i) steps[i - start_index] = outline->step_dir(i); if (end_index > 0) for (; i < end_index + len; ++i) steps[i - start_index] = outline->step_dir(i - len); } other_end = nullptr; delete close(); }
// 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; } }
DLLSYM void block_edges( //get edges in a block IMAGE *t_image, //threshold image PDBLK *block, //block in image ICOORD page_tr //corner of page ) { 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]; 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_crackedges(free_cracks); //really free them free_cracks = NULL; delete [] ptrline; }
// 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); } }
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 }
// 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(); }
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; }
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); } }
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 }
inT16 C_OUTLINE::winding_number( //winding number ICOORD point //point to wind around ) const { inT16 stepindex; //index to cstep inT16 count; //winding count ICOORD vec; //to current point ICOORD stepvec; //step vector inT32 cross; //cross product vec = start - point; //vector to it count = 0; for (stepindex = 0; stepindex < stepcount; stepindex++) { stepvec = step (stepindex); //get the step //crossing the line if (vec.y () <= 0 && vec.y () + stepvec.y () > 0) { cross = vec * stepvec; //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 () + stepvec.y () <= 0) { cross = vec * stepvec; if (cross < 0) count--; //crossing back else if (cross == 0) return INTERSECTING; //illegal } vec += stepvec; //sum vectors } return count; //winding number }
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; }
// 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; }