tesseract
3.03
|
00001 /********************************************************************** 00002 * File: oldbasel.cpp (Formerly oldbl.c) 00003 * Description: A re-implementation of the old baseline algorithm. 00004 * Author: Ray Smith 00005 * Created: Wed Oct 6 09:41:48 BST 1993 00006 * 00007 * (C) Copyright 1993, Hewlett-Packard Ltd. 00008 ** Licensed under the Apache License, Version 2.0 (the "License"); 00009 ** you may not use this file except in compliance with the License. 00010 ** You may obtain a copy of the License at 00011 ** http://www.apache.org/licenses/LICENSE-2.0 00012 ** Unless required by applicable law or agreed to in writing, software 00013 ** distributed under the License is distributed on an "AS IS" BASIS, 00014 ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 00015 ** See the License for the specific language governing permissions and 00016 ** limitations under the License. 00017 * 00018 **********************************************************************/ 00019 00020 #include "ccstruct.h" 00021 #include "statistc.h" 00022 #include "quadlsq.h" 00023 #include "detlinefit.h" 00024 #include "makerow.h" 00025 #include "drawtord.h" 00026 #include "oldbasel.h" 00027 #include "textord.h" 00028 #include "tprintf.h" 00029 00030 // Include automatically generated configuration file if running autoconf. 00031 #ifdef HAVE_CONFIG_H 00032 #include "config_auto.h" 00033 #endif 00034 00035 #define EXTERN 00036 00037 EXTERN BOOL_VAR (textord_really_old_xheight, FALSE, 00038 "Use original wiseowl xheight"); 00039 EXTERN BOOL_VAR (textord_oldbl_debug, FALSE, "Debug old baseline generation"); 00040 EXTERN BOOL_VAR (textord_debug_baselines, FALSE, "Debug baseline generation"); 00041 EXTERN BOOL_VAR (textord_oldbl_paradef, TRUE, "Use para default mechanism"); 00042 EXTERN BOOL_VAR (textord_oldbl_split_splines, TRUE, "Split stepped splines"); 00043 EXTERN BOOL_VAR (textord_oldbl_merge_parts, TRUE, "Merge suspect partitions"); 00044 EXTERN BOOL_VAR (oldbl_corrfix, TRUE, "Improve correlation of heights"); 00045 EXTERN BOOL_VAR (oldbl_xhfix, FALSE, 00046 "Fix bug in modes threshold for xheights"); 00047 EXTERN BOOL_VAR(textord_ocropus_mode, FALSE, "Make baselines for ocropus"); 00048 EXTERN double_VAR (oldbl_xhfract, 0.4, "Fraction of est allowed in calc"); 00049 EXTERN INT_VAR (oldbl_holed_losscount, 10, 00050 "Max lost before fallback line used"); 00051 EXTERN double_VAR (oldbl_dot_error_size, 1.26, "Max aspect ratio of a dot"); 00052 EXTERN double_VAR (textord_oldbl_jumplimit, 0.15, 00053 "X fraction for new partition"); 00054 00055 #define TURNLIMIT 1 /*min size for turning point */ 00056 #define X_HEIGHT_FRACTION 0.7 /*x-height/caps height */ 00057 #define DESCENDER_FRACTION 0.5 /*descender/x-height */ 00058 #define MIN_ASC_FRACTION 0.20 /*min size of ascenders */ 00059 #define MIN_DESC_FRACTION 0.25 /*min size of descenders */ 00060 #define MINASCRISE 2.0 /*min ascender/desc step */ 00061 #define MAXHEIGHTVARIANCE 0.15 /*accepted variation in x-height */ 00062 #define MAXHEIGHT 300 /*max blob height */ 00063 #define MAXOVERLAP 0.1 /*max 10% missed overlap */ 00064 #define MAXBADRUN 2 /*max non best for failed */ 00065 #define HEIGHTBUCKETS 200 /* Num of buckets */ 00066 #define DELTAHEIGHT 5.0 /* Small amount of diff */ 00067 #define GOODHEIGHT 5 00068 #define MAXLOOPS 10 00069 #define MODENUM 10 00070 #define MAXPARTS 6 00071 #define SPLINESIZE 23 00072 00073 #define ABS(x) ((x)<0 ? (-(x)) : (x)) 00074 00075 namespace tesseract { 00076 00077 /********************************************************************** 00078 * make_old_baselines 00079 * 00080 * Top level function to make baselines the old way. 00081 **********************************************************************/ 00082 00083 void Textord::make_old_baselines(TO_BLOCK *block, // block to do 00084 BOOL8 testing_on, // correct orientation 00085 float gradient) { 00086 QSPLINE *prev_baseline; // baseline of previous row 00087 TO_ROW *row; // current row 00088 TO_ROW_IT row_it = block->get_rows(); 00089 BLOBNBOX_IT blob_it; 00090 00091 prev_baseline = NULL; // nothing yet 00092 for (row_it.mark_cycle_pt(); !row_it.cycled_list(); row_it.forward()) { 00093 row = row_it.data(); 00094 find_textlines(block, row, 2, NULL); 00095 if (row->xheight <= 0 && prev_baseline != NULL) 00096 find_textlines(block, row, 2, prev_baseline); 00097 if (row->xheight > 0) { // was a good one 00098 prev_baseline = &row->baseline; 00099 } else { 00100 prev_baseline = NULL; 00101 blob_it.set_to_list(row->blob_list()); 00102 if (textord_debug_baselines) 00103 tprintf("Row baseline generation failed on row at (%d,%d)\n", 00104 blob_it.data()->bounding_box().left(), 00105 blob_it.data()->bounding_box().bottom()); 00106 } 00107 } 00108 correlate_lines(block, gradient); 00109 block->block->set_xheight(block->xheight); 00110 } 00111 00112 00113 /********************************************************************** 00114 * correlate_lines 00115 * 00116 * Correlate the x-heights and ascender heights of a block to fill-in 00117 * the ascender height and descender height for rows without one. 00118 * Also fix baselines of rows without a decent fit. 00119 **********************************************************************/ 00120 00121 void Textord::correlate_lines(TO_BLOCK *block, float gradient) { 00122 TO_ROW **rows; //array of ptrs 00123 int rowcount; /*no of rows to do */ 00124 register int rowindex; /*no of row */ 00125 //iterator 00126 TO_ROW_IT row_it = block->get_rows (); 00127 00128 rowcount = row_it.length (); 00129 if (rowcount == 0) { 00130 //default value 00131 block->xheight = block->line_size; 00132 return; /*none to do */ 00133 } 00134 rows = (TO_ROW **) alloc_mem (rowcount * sizeof (TO_ROW *)); 00135 rowindex = 0; 00136 for (row_it.mark_cycle_pt (); !row_it.cycled_list (); row_it.forward ()) 00137 //make array 00138 rows[rowindex++] = row_it.data (); 00139 00140 /*try to fix bad lines */ 00141 correlate_neighbours(block, rows, rowcount); 00142 00143 if (textord_really_old_xheight || textord_old_xheight) { 00144 block->xheight = (float) correlate_with_stats(rows, rowcount, block); 00145 if (block->xheight <= 0) 00146 block->xheight = block->line_size * tesseract::CCStruct::kXHeightFraction; 00147 if (block->xheight < textord_min_xheight) 00148 block->xheight = (float) textord_min_xheight; 00149 } else { 00150 compute_block_xheight(block, gradient); 00151 } 00152 00153 free_mem(rows); 00154 } 00155 00156 00157 /********************************************************************** 00158 * correlate_neighbours 00159 * 00160 * Try to fix rows that had a bad spline fit by using neighbours. 00161 **********************************************************************/ 00162 00163 void Textord::correlate_neighbours(TO_BLOCK *block, // block rows are in. 00164 TO_ROW **rows, // rows of block. 00165 int rowcount) { // no of rows to do. 00166 TO_ROW *row; /*current row */ 00167 register int rowindex; /*no of row */ 00168 register int otherrow; /*second row */ 00169 int upperrow; /*row above to use */ 00170 int lowerrow; /*row below to use */ 00171 float biggest; 00172 00173 for (rowindex = 0; rowindex < rowcount; rowindex++) { 00174 row = rows[rowindex]; /*current row */ 00175 if (row->xheight < 0) { 00176 /*quadratic failed */ 00177 for (otherrow = rowindex - 2; 00178 otherrow >= 0 00179 && (rows[otherrow]->xheight < 0.0 00180 || !row->baseline.overlap (&rows[otherrow]->baseline, 00181 MAXOVERLAP)); otherrow--); 00182 upperrow = otherrow; /*decent row above */ 00183 for (otherrow = rowindex + 1; 00184 otherrow < rowcount 00185 && (rows[otherrow]->xheight < 0.0 00186 || !row->baseline.overlap (&rows[otherrow]->baseline, 00187 MAXOVERLAP)); otherrow++); 00188 lowerrow = otherrow; /*decent row below */ 00189 if (upperrow >= 0) 00190 find_textlines(block, row, 2, &rows[upperrow]->baseline); 00191 if (row->xheight < 0 && lowerrow < rowcount) 00192 find_textlines(block, row, 2, &rows[lowerrow]->baseline); 00193 if (row->xheight < 0) { 00194 if (upperrow >= 0) 00195 find_textlines(block, row, 1, &rows[upperrow]->baseline); 00196 else if (lowerrow < rowcount) 00197 find_textlines(block, row, 1, &rows[lowerrow]->baseline); 00198 } 00199 } 00200 } 00201 00202 for (biggest = 0.0f, rowindex = 0; rowindex < rowcount; rowindex++) { 00203 row = rows[rowindex]; /*current row */ 00204 if (row->xheight < 0) /*linear failed */ 00205 /*make do */ 00206 row->xheight = -row->xheight; 00207 biggest = MAX (biggest, row->xheight); 00208 } 00209 } 00210 00211 00212 /********************************************************************** 00213 * correlate_with_stats 00214 * 00215 * correlate the x-heights and ascender heights of a block to fill-in 00216 * the ascender height and descender height for rows without one. 00217 **********************************************************************/ 00218 00219 int Textord::correlate_with_stats(TO_ROW **rows, // rows of block. 00220 int rowcount, // no of rows to do. 00221 TO_BLOCK* block) { 00222 TO_ROW *row; /*current row */ 00223 register int rowindex; /*no of row */ 00224 float lineheight; /*mean x-height */ 00225 float ascheight; /*average ascenders */ 00226 float minascheight; /*min allowed ascheight */ 00227 int xcount; /*no of samples for xheight */ 00228 float fullheight; /*mean top height */ 00229 int fullcount; /*no of samples */ 00230 float descheight; /*mean descender drop */ 00231 float mindescheight; /*min allowed descheight */ 00232 int desccount; /*no of samples */ 00233 00234 /*no samples */ 00235 xcount = fullcount = desccount = 0; 00236 lineheight = ascheight = fullheight = descheight = 0.0; 00237 for (rowindex = 0; rowindex < rowcount; rowindex++) { 00238 row = rows[rowindex]; /*current row */ 00239 if (row->ascrise > 0.0) { /*got ascenders? */ 00240 lineheight += row->xheight;/*average x-heights */ 00241 ascheight += row->ascrise; /*average ascenders */ 00242 xcount++; 00243 } 00244 else { 00245 fullheight += row->xheight;/*assume full height */ 00246 fullcount++; 00247 } 00248 if (row->descdrop < 0.0) { /*got descenders? */ 00249 /*average descenders */ 00250 descheight += row->descdrop; 00251 desccount++; 00252 } 00253 } 00254 00255 if (xcount > 0 && (!oldbl_corrfix || xcount >= fullcount)) { 00256 lineheight /= xcount; /*average x-height */ 00257 /*average caps height */ 00258 fullheight = lineheight + ascheight / xcount; 00259 /*must be decent size */ 00260 if (fullheight < lineheight * (1 + MIN_ASC_FRACTION)) 00261 fullheight = lineheight * (1 + MIN_ASC_FRACTION); 00262 } 00263 else { 00264 fullheight /= fullcount; /*average max height */ 00265 /*guess x-height */ 00266 lineheight = fullheight * X_HEIGHT_FRACTION; 00267 } 00268 if (desccount > 0 && (!oldbl_corrfix || desccount >= rowcount / 2)) 00269 descheight /= desccount; /*average descenders */ 00270 else 00271 /*guess descenders */ 00272 descheight = -lineheight * DESCENDER_FRACTION; 00273 00274 if (lineheight > 0.0f) 00275 block->block->set_cell_over_xheight((fullheight - descheight) / lineheight); 00276 00277 minascheight = lineheight * MIN_ASC_FRACTION; 00278 mindescheight = -lineheight * MIN_DESC_FRACTION; 00279 for (rowindex = 0; rowindex < rowcount; rowindex++) { 00280 row = rows[rowindex]; /*do each row */ 00281 row->all_caps = FALSE; 00282 if (row->ascrise / row->xheight < MIN_ASC_FRACTION) { 00283 /*no ascenders */ 00284 if (row->xheight >= lineheight * (1 - MAXHEIGHTVARIANCE) 00285 && row->xheight <= lineheight * (1 + MAXHEIGHTVARIANCE)) { 00286 row->ascrise = fullheight - lineheight; 00287 /*set to average */ 00288 row->xheight = lineheight; 00289 00290 } 00291 else if (row->xheight >= fullheight * (1 - MAXHEIGHTVARIANCE) 00292 && row->xheight <= fullheight * (1 + MAXHEIGHTVARIANCE)) { 00293 row->ascrise = row->xheight - lineheight; 00294 /*set to average */ 00295 row->xheight = lineheight; 00296 row->all_caps = TRUE; 00297 } 00298 else { 00299 row->ascrise = (fullheight - lineheight) * row->xheight 00300 / fullheight; 00301 /*scale it */ 00302 row->xheight -= row->ascrise; 00303 row->all_caps = TRUE; 00304 } 00305 if (row->ascrise < minascheight) 00306 row->ascrise = 00307 row->xheight * ((1.0 - X_HEIGHT_FRACTION) / X_HEIGHT_FRACTION); 00308 } 00309 if (row->descdrop > mindescheight) { 00310 if (row->xheight >= lineheight * (1 - MAXHEIGHTVARIANCE) 00311 && row->xheight <= lineheight * (1 + MAXHEIGHTVARIANCE)) 00312 /*set to average */ 00313 row->descdrop = descheight; 00314 else 00315 row->descdrop = -row->xheight * DESCENDER_FRACTION; 00316 } 00317 } 00318 return (int) lineheight; //block xheight 00319 } 00320 00321 00322 /********************************************************************** 00323 * find_textlines 00324 * 00325 * Compute the baseline for the given row. 00326 **********************************************************************/ 00327 00328 void Textord::find_textlines(TO_BLOCK *block, // block row is in 00329 TO_ROW *row, // row to do 00330 int degree, // required approximation 00331 QSPLINE *spline) { // starting spline 00332 int partcount; /*no of partitions of */ 00333 BOOL8 holed_line = FALSE; //lost too many blobs 00334 int bestpart; /*biggest partition */ 00335 char *partids; /*partition no of each blob */ 00336 int partsizes[MAXPARTS]; /*no in each partition */ 00337 int lineheight; /*guessed x-height */ 00338 float jumplimit; /*allowed delta change */ 00339 int *xcoords; /*useful sample points */ 00340 int *ycoords; /*useful sample points */ 00341 TBOX *blobcoords; /*edges of blob rectangles */ 00342 int blobcount; /*no of blobs on line */ 00343 float *ydiffs; /*diffs from 1st approx */ 00344 int pointcount; /*no of coords */ 00345 int xstarts[SPLINESIZE + 1]; //segment boundaries 00346 int segments; //no of segments 00347 00348 //no of blobs in row 00349 blobcount = row->blob_list ()->length (); 00350 partids = (char *) alloc_mem (blobcount * sizeof (char)); 00351 xcoords = (int *) alloc_mem (blobcount * sizeof (int)); 00352 ycoords = (int *) alloc_mem (blobcount * sizeof (int)); 00353 blobcoords = (TBOX *) alloc_mem (blobcount * sizeof (TBOX)); 00354 ydiffs = (float *) alloc_mem (blobcount * sizeof (float)); 00355 00356 lineheight = get_blob_coords (row, (int) block->line_size, blobcoords, 00357 holed_line, blobcount); 00358 /*limit for line change */ 00359 jumplimit = lineheight * textord_oldbl_jumplimit; 00360 if (jumplimit < MINASCRISE) 00361 jumplimit = MINASCRISE; 00362 00363 if (textord_oldbl_debug) { 00364 tprintf 00365 ("\nInput height=%g, Estimate x-height=%d pixels, jumplimit=%.2f\n", 00366 block->line_size, lineheight, jumplimit); 00367 } 00368 if (holed_line) 00369 make_holed_baseline (blobcoords, blobcount, spline, &row->baseline, 00370 row->line_m ()); 00371 else 00372 make_first_baseline (blobcoords, blobcount, 00373 xcoords, ycoords, spline, &row->baseline, jumplimit); 00374 #ifndef GRAPHICS_DISABLED 00375 if (textord_show_final_rows) 00376 row->baseline.plot (to_win, ScrollView::GOLDENROD); 00377 #endif 00378 if (blobcount > 1) { 00379 bestpart = partition_line (blobcoords, blobcount, 00380 &partcount, partids, partsizes, 00381 &row->baseline, jumplimit, ydiffs); 00382 pointcount = partition_coords (blobcoords, blobcount, 00383 partids, bestpart, xcoords, ycoords); 00384 segments = segment_spline (blobcoords, blobcount, 00385 xcoords, ycoords, 00386 degree, pointcount, xstarts); 00387 if (!holed_line) { 00388 do { 00389 row->baseline = QSPLINE (xstarts, segments, 00390 xcoords, ycoords, pointcount, degree); 00391 } 00392 while (textord_oldbl_split_splines 00393 && split_stepped_spline (&row->baseline, jumplimit / 2, 00394 xcoords, xstarts, segments)); 00395 } 00396 find_lesser_parts(row, 00397 blobcoords, 00398 blobcount, 00399 partids, 00400 partsizes, 00401 partcount, 00402 bestpart); 00403 00404 } 00405 else { 00406 row->xheight = -1.0f; /*failed */ 00407 row->descdrop = 0.0f; 00408 row->ascrise = 0.0f; 00409 } 00410 row->baseline.extrapolate (row->line_m (), 00411 block->block->bounding_box ().left (), 00412 block->block->bounding_box ().right ()); 00413 00414 if (textord_really_old_xheight) { 00415 old_first_xheight (row, blobcoords, lineheight, 00416 blobcount, &row->baseline, jumplimit); 00417 } else if (textord_old_xheight) { 00418 make_first_xheight (row, blobcoords, lineheight, (int) block->line_size, 00419 blobcount, &row->baseline, jumplimit); 00420 } else { 00421 compute_row_xheight(row, block->block->classify_rotation(), 00422 row->line_m(), block->line_size); 00423 } 00424 free_mem(partids); 00425 free_mem(xcoords); 00426 free_mem(ycoords); 00427 free_mem(blobcoords); 00428 free_mem(ydiffs); 00429 } 00430 00431 } // namespace tesseract. 00432 00433 00434 /********************************************************************** 00435 * get_blob_coords 00436 * 00437 * Fill the blobcoords array with the coordinates of the blobs 00438 * in the row. The return value is the first guess at the line height. 00439 **********************************************************************/ 00440 00441 int get_blob_coords( //get boxes 00442 TO_ROW *row, //row to use 00443 inT32 lineheight, //block level 00444 TBOX *blobcoords, //ouput boxes 00445 BOOL8 &holed_line, //lost a lot of blobs 00446 int &outcount //no of real blobs 00447 ) { 00448 //blobs 00449 BLOBNBOX_IT blob_it = row->blob_list (); 00450 register int blobindex; /*no along text line */ 00451 int losscount; //lost blobs 00452 int maxlosscount; //greatest lost blobs 00453 /*height stat collection */ 00454 STATS heightstat (0, MAXHEIGHT); 00455 00456 if (blob_it.empty ()) 00457 return 0; //none 00458 maxlosscount = 0; 00459 losscount = 0; 00460 blob_it.mark_cycle_pt (); 00461 blobindex = 0; 00462 do { 00463 blobcoords[blobindex] = box_next_pre_chopped (&blob_it); 00464 if (blobcoords[blobindex].height () > lineheight * 0.25) 00465 heightstat.add (blobcoords[blobindex].height (), 1); 00466 if (blobindex == 0 00467 || blobcoords[blobindex].height () > lineheight * 0.25 00468 || blob_it.cycled_list ()) { 00469 blobindex++; /*no of merged blobs */ 00470 losscount = 0; 00471 } 00472 else { 00473 if (blobcoords[blobindex].height () 00474 < blobcoords[blobindex].width () * oldbl_dot_error_size 00475 && blobcoords[blobindex].width () 00476 < blobcoords[blobindex].height () * oldbl_dot_error_size) { 00477 //counts as dot 00478 blobindex++; 00479 losscount = 0; 00480 } 00481 else { 00482 losscount++; //lost it 00483 if (losscount > maxlosscount) 00484 //remember max 00485 maxlosscount = losscount; 00486 } 00487 } 00488 } 00489 while (!blob_it.cycled_list ()); 00490 00491 holed_line = maxlosscount > oldbl_holed_losscount; 00492 outcount = blobindex; /*total blobs */ 00493 00494 if (heightstat.get_total () > 1) 00495 /*guess x-height */ 00496 return (int) heightstat.ile (0.25); 00497 else 00498 return blobcoords[0].height (); 00499 } 00500 00501 00502 /********************************************************************** 00503 * make_first_baseline 00504 * 00505 * Make the first estimate at a baseline, either by shifting 00506 * a supplied previous spline, or by doing a piecewise linear 00507 * approximation using all the blobs. 00508 **********************************************************************/ 00509 00510 void 00511 make_first_baseline ( //initial approximation 00512 TBOX blobcoords[], /*blob bounding boxes */ 00513 int blobcount, /*no of blobcoords */ 00514 int xcoords[], /*coords for spline */ 00515 int ycoords[], /*approximator */ 00516 QSPLINE * spline, /*initial spline */ 00517 QSPLINE * baseline, /*output spline */ 00518 float jumplimit /*guess half descenders */ 00519 ) { 00520 int leftedge; /*left edge of line */ 00521 int rightedge; /*right edge of line */ 00522 int blobindex; /*current blob */ 00523 int segment; /*current segment */ 00524 float prevy, thisy, nexty; /*3 y coords */ 00525 float y1, y2, y3; /*3 smooth blobs */ 00526 float maxmax, minmin; /*absolute limits */ 00527 int x2 = 0; /*right edge of old y3 */ 00528 int ycount; /*no of ycoords in use */ 00529 float yturns[SPLINESIZE]; /*y coords of turn pts */ 00530 int xturns[SPLINESIZE]; /*xcoords of turn pts */ 00531 int xstarts[SPLINESIZE + 1]; 00532 int segments; //no of segments 00533 ICOORD shift; //shift of spline 00534 00535 prevy = 0; 00536 /*left edge of row */ 00537 leftedge = blobcoords[0].left (); 00538 /*right edge of line */ 00539 rightedge = blobcoords[blobcount - 1].right (); 00540 if (spline == NULL /*no given spline */ 00541 || spline->segments < 3 /*or trivial */ 00542 /*or too non-overlap */ 00543 || spline->xcoords[1] > leftedge + MAXOVERLAP * (rightedge - leftedge) 00544 || spline->xcoords[spline->segments - 1] < rightedge 00545 - MAXOVERLAP * (rightedge - leftedge)) { 00546 if (textord_oldbl_paradef) 00547 return; //use default 00548 xstarts[0] = blobcoords[0].left () - 1; 00549 for (blobindex = 0; blobindex < blobcount; blobindex++) { 00550 xcoords[blobindex] = (blobcoords[blobindex].left () 00551 + blobcoords[blobindex].right ()) / 2; 00552 ycoords[blobindex] = blobcoords[blobindex].bottom (); 00553 } 00554 xstarts[1] = blobcoords[blobcount - 1].right () + 1; 00555 segments = 1; /*no of segments */ 00556 00557 /*linear */ 00558 *baseline = QSPLINE (xstarts, segments, xcoords, ycoords, blobcount, 1); 00559 00560 if (blobcount >= 3) { 00561 y1 = y2 = y3 = 0.0f; 00562 ycount = 0; 00563 segment = 0; /*no of segments */ 00564 maxmax = minmin = 0.0f; 00565 thisy = ycoords[0] - baseline->y (xcoords[0]); 00566 nexty = ycoords[1] - baseline->y (xcoords[1]); 00567 for (blobindex = 2; blobindex < blobcount; blobindex++) { 00568 prevy = thisy; /*shift ycoords */ 00569 thisy = nexty; 00570 nexty = ycoords[blobindex] - baseline->y (xcoords[blobindex]); 00571 /*middle of smooth y */ 00572 if (ABS (thisy - prevy) < jumplimit && ABS (thisy - nexty) < jumplimit) { 00573 y1 = y2; /*shift window */ 00574 y2 = y3; 00575 y3 = thisy; /*middle point */ 00576 ycount++; 00577 /*local max */ 00578 if (ycount >= 3 && ((y1 < y2 && y2 >= y3) 00579 /*local min */ 00580 || (y1 > y2 && y2 <= y3))) { 00581 if (segment < SPLINESIZE - 2) { 00582 /*turning pt */ 00583 xturns[segment] = x2; 00584 yturns[segment] = y2; 00585 segment++; /*no of spline segs */ 00586 } 00587 } 00588 if (ycount == 1) { 00589 maxmax = minmin = y3;/*initialise limits */ 00590 } 00591 else { 00592 if (y3 > maxmax) 00593 maxmax = y3; /*biggest max */ 00594 if (y3 < minmin) 00595 minmin = y3; /*smallest min */ 00596 } 00597 /*possible turning pt */ 00598 x2 = blobcoords[blobindex - 1].right (); 00599 } 00600 } 00601 00602 jumplimit *= 1.2; 00603 /*must be wavy */ 00604 if (maxmax - minmin > jumplimit) { 00605 ycount = segment; /*no of segments */ 00606 for (blobindex = 0, segment = 1; blobindex < ycount; 00607 blobindex++) { 00608 if (yturns[blobindex] > minmin + jumplimit 00609 || yturns[blobindex] < maxmax - jumplimit) { 00610 /*significant peak */ 00611 if (segment == 1 00612 || yturns[blobindex] > prevy + jumplimit 00613 || yturns[blobindex] < prevy - jumplimit) { 00614 /*different to previous */ 00615 xstarts[segment] = xturns[blobindex]; 00616 segment++; 00617 prevy = yturns[blobindex]; 00618 } 00619 /*bigger max */ 00620 else if ((prevy > minmin + jumplimit && yturns[blobindex] > prevy) 00621 /*smaller min */ 00622 || (prevy < maxmax - jumplimit && yturns[blobindex] < prevy)) { 00623 xstarts[segment - 1] = xturns[blobindex]; 00624 /*improved previous */ 00625 prevy = yturns[blobindex]; 00626 } 00627 } 00628 } 00629 xstarts[segment] = blobcoords[blobcount - 1].right () + 1; 00630 segments = segment; /*no of segments */ 00631 /*linear */ 00632 *baseline = QSPLINE (xstarts, segments, xcoords, ycoords, blobcount, 1); 00633 } 00634 } 00635 } 00636 else { 00637 *baseline = *spline; /*copy it */ 00638 shift = ICOORD (0, (inT16) (blobcoords[0].bottom () 00639 - spline->y (blobcoords[0].right ()))); 00640 baseline->move (shift); 00641 } 00642 } 00643 00644 00645 /********************************************************************** 00646 * make_holed_baseline 00647 * 00648 * Make the first estimate at a baseline, either by shifting 00649 * a supplied previous spline, or by doing a piecewise linear 00650 * approximation using all the blobs. 00651 **********************************************************************/ 00652 00653 void 00654 make_holed_baseline ( //initial approximation 00655 TBOX blobcoords[], /*blob bounding boxes */ 00656 int blobcount, /*no of blobcoords */ 00657 QSPLINE * spline, /*initial spline */ 00658 QSPLINE * baseline, /*output spline */ 00659 float gradient //of line 00660 ) { 00661 int leftedge; /*left edge of line */ 00662 int rightedge; /*right edge of line */ 00663 int blobindex; /*current blob */ 00664 float x; //centre of row 00665 ICOORD shift; //shift of spline 00666 00667 tesseract::DetLineFit lms; // straight baseline 00668 inT32 xstarts[2]; //straight line 00669 double coeffs[3]; 00670 float c; //line parameter 00671 00672 /*left edge of row */ 00673 leftedge = blobcoords[0].left (); 00674 /*right edge of line */ 00675 rightedge = blobcoords[blobcount - 1].right(); 00676 for (blobindex = 0; blobindex < blobcount; blobindex++) { 00677 lms.Add(ICOORD((blobcoords[blobindex].left() + 00678 blobcoords[blobindex].right()) / 2, 00679 blobcoords[blobindex].bottom())); 00680 } 00681 lms.ConstrainedFit(gradient, &c); 00682 xstarts[0] = leftedge; 00683 xstarts[1] = rightedge; 00684 coeffs[0] = 0; 00685 coeffs[1] = gradient; 00686 coeffs[2] = c; 00687 *baseline = QSPLINE (1, xstarts, coeffs); 00688 if (spline != NULL /*no given spline */ 00689 && spline->segments >= 3 /*or trivial */ 00690 /*or too non-overlap */ 00691 && spline->xcoords[1] <= leftedge + MAXOVERLAP * (rightedge - leftedge) 00692 && spline->xcoords[spline->segments - 1] >= rightedge 00693 - MAXOVERLAP * (rightedge - leftedge)) { 00694 *baseline = *spline; /*copy it */ 00695 x = (leftedge + rightedge) / 2.0; 00696 shift = ICOORD (0, (inT16) (gradient * x + c - spline->y (x))); 00697 baseline->move (shift); 00698 } 00699 } 00700 00701 00702 /********************************************************************** 00703 * partition_line 00704 * 00705 * Partition a row of blobs into different groups of continuous 00706 * y position. jumplimit specifies the max allowable limit on a jump 00707 * before a new partition is started. 00708 * The return value is the biggest partition 00709 **********************************************************************/ 00710 00711 int 00712 partition_line ( //partition blobs 00713 TBOX blobcoords[], //bounding boxes 00714 int blobcount, /*no of blobs on row */ 00715 int *numparts, /*number of partitions */ 00716 char partids[], /*partition no of each blob */ 00717 int partsizes[], /*no in each partition */ 00718 QSPLINE * spline, /*curve to fit to */ 00719 float jumplimit, /*allowed delta change */ 00720 float ydiffs[] /*diff from spline */ 00721 ) { 00722 register int blobindex; /*no along text line */ 00723 int bestpart; /*best new partition */ 00724 int biggestpart; /*part with most members */ 00725 float diff; /*difference from line */ 00726 int startx; /*index of start blob */ 00727 float partdiffs[MAXPARTS]; /*step between parts */ 00728 00729 for (bestpart = 0; bestpart < MAXPARTS; bestpart++) 00730 partsizes[bestpart] = 0; /*zero them all */ 00731 00732 startx = get_ydiffs (blobcoords, blobcount, spline, ydiffs); 00733 *numparts = 1; /*1 partition */ 00734 bestpart = -1; /*first point */ 00735 float drift = 0.0f; 00736 float last_delta = 0.0f; 00737 for (blobindex = startx; blobindex < blobcount; blobindex++) { 00738 /*do each blob in row */ 00739 diff = ydiffs[blobindex]; /*diff from line */ 00740 if (textord_oldbl_debug) { 00741 tprintf ("%d(%d,%d), ", blobindex, 00742 blobcoords[blobindex].left (), 00743 blobcoords[blobindex].bottom ()); 00744 } 00745 bestpart = choose_partition(diff, partdiffs, bestpart, jumplimit, 00746 &drift, &last_delta, numparts); 00747 /*record partition */ 00748 partids[blobindex] = bestpart; 00749 partsizes[bestpart]++; /*another in it */ 00750 } 00751 00752 bestpart = -1; /*first point */ 00753 drift = 0.0f; 00754 last_delta = 0.0f; 00755 partsizes[0]--; /*doing 1st pt again */ 00756 /*do each blob in row */ 00757 for (blobindex = startx; blobindex >= 0; blobindex--) { 00758 diff = ydiffs[blobindex]; /*diff from line */ 00759 if (textord_oldbl_debug) { 00760 tprintf ("%d(%d,%d), ", blobindex, 00761 blobcoords[blobindex].left (), 00762 blobcoords[blobindex].bottom ()); 00763 } 00764 bestpart = choose_partition(diff, partdiffs, bestpart, jumplimit, 00765 &drift, &last_delta, numparts); 00766 /*record partition */ 00767 partids[blobindex] = bestpart; 00768 partsizes[bestpart]++; /*another in it */ 00769 } 00770 00771 for (biggestpart = 0, bestpart = 1; bestpart < *numparts; bestpart++) 00772 if (partsizes[bestpart] >= partsizes[biggestpart]) 00773 biggestpart = bestpart; /*new biggest */ 00774 if (textord_oldbl_merge_parts) 00775 merge_oldbl_parts(blobcoords, 00776 blobcount, 00777 partids, 00778 partsizes, 00779 biggestpart, 00780 jumplimit); 00781 return biggestpart; /*biggest partition */ 00782 } 00783 00784 00785 /********************************************************************** 00786 * merge_oldbl_parts 00787 * 00788 * For any adjacent group of blobs in a different part, put them in the 00789 * main part if they fit closely to neighbours in the main part. 00790 **********************************************************************/ 00791 00792 void 00793 merge_oldbl_parts ( //partition blobs 00794 TBOX blobcoords[], //bounding boxes 00795 int blobcount, /*no of blobs on row */ 00796 char partids[], /*partition no of each blob */ 00797 int partsizes[], /*no in each partition */ 00798 int biggestpart, //major partition 00799 float jumplimit /*allowed delta change */ 00800 ) { 00801 BOOL8 found_one; //found a bestpart blob 00802 BOOL8 close_one; //found was close enough 00803 register int blobindex; /*no along text line */ 00804 int prevpart; //previous iteration 00805 int runlength; //no in this part 00806 float diff; /*difference from line */ 00807 int startx; /*index of start blob */ 00808 int test_blob; //another index 00809 FCOORD coord; //blob coordinate 00810 float m, c; //fitted line 00811 QLSQ stats; //line stuff 00812 00813 prevpart = biggestpart; 00814 runlength = 0; 00815 startx = 0; 00816 for (blobindex = 0; blobindex < blobcount; blobindex++) { 00817 if (partids[blobindex] != prevpart) { 00818 // tprintf("Partition change at (%d,%d) from %d to %d after run of %d\n", 00819 // blobcoords[blobindex].left(),blobcoords[blobindex].bottom(), 00820 // prevpart,partids[blobindex],runlength); 00821 if (prevpart != biggestpart && runlength > MAXBADRUN) { 00822 stats.clear (); 00823 for (test_blob = startx; test_blob < blobindex; test_blob++) { 00824 coord = FCOORD ((blobcoords[test_blob].left () 00825 + blobcoords[test_blob].right ()) / 2.0, 00826 blobcoords[test_blob].bottom ()); 00827 stats.add (coord.x (), coord.y ()); 00828 } 00829 stats.fit (1); 00830 m = stats.get_b (); 00831 c = stats.get_c (); 00832 if (textord_oldbl_debug) 00833 tprintf ("Fitted line y=%g x + %g\n", m, c); 00834 found_one = FALSE; 00835 close_one = FALSE; 00836 for (test_blob = 1; !found_one 00837 && (startx - test_blob >= 0 00838 || blobindex + test_blob <= blobcount); test_blob++) { 00839 if (startx - test_blob >= 0 00840 && partids[startx - test_blob] == biggestpart) { 00841 found_one = TRUE; 00842 coord = FCOORD ((blobcoords[startx - test_blob].left () 00843 + blobcoords[startx - 00844 test_blob].right ()) / 00845 2.0, 00846 blobcoords[startx - 00847 test_blob].bottom ()); 00848 diff = m * coord.x () + c - coord.y (); 00849 if (textord_oldbl_debug) 00850 tprintf 00851 ("Diff of common blob to suspect part=%g at (%g,%g)\n", 00852 diff, coord.x (), coord.y ()); 00853 if (diff < jumplimit && -diff < jumplimit) 00854 close_one = TRUE; 00855 } 00856 if (blobindex + test_blob <= blobcount 00857 && partids[blobindex + test_blob - 1] == biggestpart) { 00858 found_one = TRUE; 00859 coord = 00860 FCOORD ((blobcoords[blobindex + test_blob - 1]. 00861 left () + blobcoords[blobindex + test_blob - 00862 1].right ()) / 2.0, 00863 blobcoords[blobindex + test_blob - 00864 1].bottom ()); 00865 diff = m * coord.x () + c - coord.y (); 00866 if (textord_oldbl_debug) 00867 tprintf 00868 ("Diff of common blob to suspect part=%g at (%g,%g)\n", 00869 diff, coord.x (), coord.y ()); 00870 if (diff < jumplimit && -diff < jumplimit) 00871 close_one = TRUE; 00872 } 00873 } 00874 if (close_one) { 00875 if (textord_oldbl_debug) 00876 tprintf 00877 ("Merged %d blobs back into part %d from %d starting at (%d,%d)\n", 00878 runlength, biggestpart, prevpart, 00879 blobcoords[startx].left (), 00880 blobcoords[startx].bottom ()); 00881 //switch sides 00882 partsizes[prevpart] -= runlength; 00883 for (test_blob = startx; test_blob < blobindex; test_blob++) 00884 partids[test_blob] = biggestpart; 00885 } 00886 } 00887 prevpart = partids[blobindex]; 00888 runlength = 1; 00889 startx = blobindex; 00890 } 00891 else 00892 runlength++; 00893 } 00894 } 00895 00896 00897 /********************************************************************** 00898 * get_ydiffs 00899 * 00900 * Get the differences between the blobs and the spline, 00901 * putting them in ydiffs. The return value is the index 00902 * of the blob in the middle of the "best behaved" region 00903 **********************************************************************/ 00904 00905 int 00906 get_ydiffs ( //evaluate differences 00907 TBOX blobcoords[], //bounding boxes 00908 int blobcount, /*no of blobs */ 00909 QSPLINE * spline, /*approximating spline */ 00910 float ydiffs[] /*output */ 00911 ) { 00912 register int blobindex; /*current blob */ 00913 int xcentre; /*xcoord */ 00914 int lastx; /*last xcentre */ 00915 float diffsum; /*sum of diffs */ 00916 float diff; /*current difference */ 00917 float drift; /*sum of spline steps */ 00918 float bestsum; /*smallest diffsum */ 00919 int bestindex; /*index of bestsum */ 00920 00921 diffsum = 0.0f; 00922 bestindex = 0; 00923 bestsum = (float) MAX_INT32; 00924 drift = 0.0f; 00925 lastx = blobcoords[0].left (); 00926 /*do each blob in row */ 00927 for (blobindex = 0; blobindex < blobcount; blobindex++) { 00928 /*centre of blob */ 00929 xcentre = (blobcoords[blobindex].left () + blobcoords[blobindex].right ()) >> 1; 00930 //step functions in spline 00931 drift += spline->step (lastx, xcentre); 00932 lastx = xcentre; 00933 diff = blobcoords[blobindex].bottom (); 00934 diff -= spline->y (xcentre); 00935 diff += drift; 00936 ydiffs[blobindex] = diff; /*store difference */ 00937 if (blobindex > 2) 00938 /*remove old one */ 00939 diffsum -= ABS (ydiffs[blobindex - 3]); 00940 diffsum += ABS (diff); /*add new one */ 00941 if (blobindex >= 2 && diffsum < bestsum) { 00942 bestsum = diffsum; /*find min sum */ 00943 bestindex = blobindex - 1; /*middle of set */ 00944 } 00945 } 00946 return bestindex; 00947 } 00948 00949 00950 /********************************************************************** 00951 * choose_partition 00952 * 00953 * Choose a partition for the point and return the index. 00954 **********************************************************************/ 00955 00956 int 00957 choose_partition ( //select partition 00958 register float diff, /*diff from spline */ 00959 float partdiffs[], /*diff on all parts */ 00960 int lastpart, /*last assigned partition */ 00961 float jumplimit, /*new part threshold */ 00962 float* drift, 00963 float* lastdelta, 00964 int *partcount /*no of partitions */ 00965 ) { 00966 register int partition; /*partition no */ 00967 int bestpart; /*best new partition */ 00968 float bestdelta; /*best gap from a part */ 00969 float delta; /*diff from part */ 00970 00971 if (lastpart < 0) { 00972 partdiffs[0] = diff; 00973 lastpart = 0; /*first point */ 00974 *drift = 0.0f; 00975 *lastdelta = 0.0f; 00976 } 00977 /*adjusted diff from part */ 00978 delta = diff - partdiffs[lastpart] - *drift; 00979 if (textord_oldbl_debug) { 00980 tprintf ("Diff=%.2f, Delta=%.3f, Drift=%.3f, ", diff, delta, *drift); 00981 } 00982 if (ABS (delta) > jumplimit / 2) { 00983 /*delta on part 0 */ 00984 bestdelta = diff - partdiffs[0] - *drift; 00985 bestpart = 0; /*0 best so far */ 00986 for (partition = 1; partition < *partcount; partition++) { 00987 delta = diff - partdiffs[partition] - *drift; 00988 if (ABS (delta) < ABS (bestdelta)) { 00989 bestdelta = delta; 00990 bestpart = partition; /*part with nearest jump */ 00991 } 00992 } 00993 delta = bestdelta; 00994 /*too far away */ 00995 if (ABS (bestdelta) > jumplimit 00996 && *partcount < MAXPARTS) { /*and spare part left */ 00997 bestpart = (*partcount)++; /*best was new one */ 00998 /*start new one */ 00999 partdiffs[bestpart] = diff - *drift; 01000 delta = 0.0f; 01001 } 01002 } 01003 else { 01004 bestpart = lastpart; /*best was last one */ 01005 } 01006 01007 if (bestpart == lastpart 01008 && (ABS (delta - *lastdelta) < jumplimit / 2 01009 || ABS (delta) < jumplimit / 2)) 01010 /*smooth the drift */ 01011 *drift = (3 * *drift + delta) / 3; 01012 *lastdelta = delta; 01013 01014 if (textord_oldbl_debug) { 01015 tprintf ("P=%d\n", bestpart); 01016 } 01017 01018 return bestpart; 01019 } 01020 01021 01023 //partitions and gives all the rest partid 0*/ 01024 // 01025 //merge_partitions(partids,partcount,blobcount,bestpart) 01026 //register char *partids; /*partition numbers*/ 01027 //int partcount; /*no of partitions*/ 01028 //int blobcount; /*no of blobs*/ 01029 //int bestpart; /*best partition*/ 01030 //{ 01031 // register int blobindex; /*no along text line*/ 01032 // int runlength; /*run of same partition*/ 01033 // int bestrun; /*biggest runlength*/ 01034 // 01035 // bestrun=0; /*no runs yet*/ 01036 // runlength=1; 01037 // for (blobindex=1;blobindex<blobcount;blobindex++) 01038 // { if (partids[blobindex]!=partids[blobindex-1]) 01039 // { if (runlength>bestrun) 01040 // bestrun=runlength; /*find biggest run*/ 01041 // runlength=1; /*new run*/ 01042 // } 01043 // else 01044 // { runlength++; 01045 // } 01046 // } 01047 // if (runlength>bestrun) 01048 // bestrun=runlength; 01049 // 01050 // for (blobindex=0;blobindex<blobcount;blobindex++) 01051 // { if (blobindex<1 01052 // || partids[blobindex]!=partids[blobindex-1]) 01053 // { if ((blobindex+1>=blobcount 01054 // || partids[blobindex]!=partids[blobindex+1]) 01055 // /*loner*/ 01056 // && (bestrun>2 || partids[blobindex]!=bestpart)) 01057 // { partids[blobindex]=partcount; /*discard loner*/ 01058 // } 01059 // else if (blobindex+1<blobcount 01060 // && partids[blobindex]==partids[blobindex+1] 01061 // /*pair*/ 01062 // && (blobindex+2>=blobcount 01063 // || partids[blobindex]!=partids[blobindex+2]) 01064 // && (bestrun>3 || partids[blobindex]!=bestpart)) 01065 // { partids[blobindex]=partcount; /*discard both*/ 01066 // partids[blobindex+1]=partcount; 01067 // } 01068 // } 01069 // } 01070 // for (blobindex=0;blobindex<blobcount;blobindex++) 01071 // { if (partids[blobindex]<partcount) 01072 // partids[blobindex]=0; /*all others together*/ 01073 // } 01074 //} 01075 01076 /********************************************************************** 01077 * partition_coords 01078 * 01079 * Get the x,y coordinates of all points in the bestpart and put them 01080 * in xcoords,ycoords. Return the number of points found. 01081 **********************************************************************/ 01082 01083 int 01084 partition_coords ( //find relevant coords 01085 TBOX blobcoords[], //bounding boxes 01086 int blobcount, /*no of blobs in row */ 01087 char partids[], /*partition no of each blob */ 01088 int bestpart, /*best new partition */ 01089 int xcoords[], /*points to work on */ 01090 int ycoords[] /*points to work on */ 01091 ) { 01092 register int blobindex; /*no along text line */ 01093 int pointcount; /*no of points */ 01094 01095 pointcount = 0; 01096 for (blobindex = 0; blobindex < blobcount; blobindex++) { 01097 if (partids[blobindex] == bestpart) { 01098 /*centre of blob */ 01099 xcoords[pointcount] = (blobcoords[blobindex].left () + blobcoords[blobindex].right ()) >> 1; 01100 ycoords[pointcount++] = blobcoords[blobindex].bottom (); 01101 } 01102 } 01103 return pointcount; /*no of points found */ 01104 } 01105 01106 01107 /********************************************************************** 01108 * segment_spline 01109 * 01110 * Segment the row at midpoints between maxima and minima of the x,y pairs. 01111 * The xstarts of the segments are returned and the number found. 01112 **********************************************************************/ 01113 01114 int 01115 segment_spline ( //make xstarts 01116 TBOX blobcoords[], //boundign boxes 01117 int blobcount, /*no of blobs in row */ 01118 int xcoords[], /*points to work on */ 01119 int ycoords[], /*points to work on */ 01120 int degree, int pointcount, /*no of points */ 01121 int xstarts[] //result 01122 ) { 01123 register int ptindex; /*no along text line */ 01124 register int segment; /*partition no */ 01125 int lastmin, lastmax; /*possible turn points */ 01126 int turnpoints[SPLINESIZE]; /*good turning points */ 01127 int turncount; /*no of turning points */ 01128 int max_x; //max specified coord 01129 01130 xstarts[0] = xcoords[0] - 1; //leftmost defined pt 01131 max_x = xcoords[pointcount - 1] + 1; 01132 if (degree < 2) 01133 pointcount = 0; 01134 turncount = 0; /*no turning points yet */ 01135 if (pointcount > 3) { 01136 ptindex = 1; 01137 lastmax = lastmin = 0; /*start with first one */ 01138 while (ptindex < pointcount - 1 && turncount < SPLINESIZE - 1) { 01139 /*minimum */ 01140 if (ycoords[ptindex - 1] > ycoords[ptindex] && ycoords[ptindex] <= ycoords[ptindex + 1]) { 01141 if (ycoords[ptindex] < ycoords[lastmax] - TURNLIMIT) { 01142 if (turncount == 0 || turnpoints[turncount - 1] != lastmax) 01143 /*new max point */ 01144 turnpoints[turncount++] = lastmax; 01145 lastmin = ptindex; /*latest minimum */ 01146 } 01147 else if (ycoords[ptindex] < ycoords[lastmin]) { 01148 lastmin = ptindex; /*lower minimum */ 01149 } 01150 } 01151 01152 /*maximum */ 01153 if (ycoords[ptindex - 1] < ycoords[ptindex] && ycoords[ptindex] >= ycoords[ptindex + 1]) { 01154 if (ycoords[ptindex] > ycoords[lastmin] + TURNLIMIT) { 01155 if (turncount == 0 || turnpoints[turncount - 1] != lastmin) 01156 /*new min point */ 01157 turnpoints[turncount++] = lastmin; 01158 lastmax = ptindex; /*latest maximum */ 01159 } 01160 else if (ycoords[ptindex] > ycoords[lastmax]) { 01161 lastmax = ptindex; /*higher maximum */ 01162 } 01163 } 01164 ptindex++; 01165 } 01166 /*possible global min */ 01167 if (ycoords[ptindex] < ycoords[lastmax] - TURNLIMIT 01168 && (turncount == 0 || turnpoints[turncount - 1] != lastmax)) { 01169 if (turncount < SPLINESIZE - 1) 01170 /*2 more turns */ 01171 turnpoints[turncount++] = lastmax; 01172 if (turncount < SPLINESIZE - 1) 01173 turnpoints[turncount++] = ptindex; 01174 } 01175 else if (ycoords[ptindex] > ycoords[lastmin] + TURNLIMIT 01176 /*possible global max */ 01177 && (turncount == 0 || turnpoints[turncount - 1] != lastmin)) { 01178 if (turncount < SPLINESIZE - 1) 01179 /*2 more turns */ 01180 turnpoints[turncount++] = lastmin; 01181 if (turncount < SPLINESIZE - 1) 01182 turnpoints[turncount++] = ptindex; 01183 } 01184 else if (turncount > 0 && turnpoints[turncount - 1] == lastmin 01185 && turncount < SPLINESIZE - 1) { 01186 if (ycoords[ptindex] > ycoords[lastmax]) 01187 turnpoints[turncount++] = ptindex; 01188 else 01189 turnpoints[turncount++] = lastmax; 01190 } 01191 else if (turncount > 0 && turnpoints[turncount - 1] == lastmax 01192 && turncount < SPLINESIZE - 1) { 01193 if (ycoords[ptindex] < ycoords[lastmin]) 01194 turnpoints[turncount++] = ptindex; 01195 else 01196 turnpoints[turncount++] = lastmin; 01197 } 01198 } 01199 01200 if (textord_oldbl_debug && turncount > 0) 01201 tprintf ("First turn is %d at (%d,%d)\n", 01202 turnpoints[0], xcoords[turnpoints[0]], ycoords[turnpoints[0]]); 01203 for (segment = 1; segment < turncount; segment++) { 01204 /*centre y coord */ 01205 lastmax = (ycoords[turnpoints[segment - 1]] + ycoords[turnpoints[segment]]) / 2; 01206 01207 /* fix alg so that it works with both rising and falling sections */ 01208 if (ycoords[turnpoints[segment - 1]] < ycoords[turnpoints[segment]]) 01209 /*find rising y centre */ 01210 for (ptindex = turnpoints[segment - 1] + 1; ptindex < turnpoints[segment] && ycoords[ptindex + 1] <= lastmax; ptindex++); 01211 else 01212 /*find falling y centre */ 01213 for (ptindex = turnpoints[segment - 1] + 1; ptindex < turnpoints[segment] && ycoords[ptindex + 1] >= lastmax; ptindex++); 01214 01215 /*centre x */ 01216 xstarts[segment] = (xcoords[ptindex - 1] + xcoords[ptindex] 01217 + xcoords[turnpoints[segment - 1]] 01218 + xcoords[turnpoints[segment]] + 2) / 4; 01219 /*halfway between turns */ 01220 if (textord_oldbl_debug) 01221 tprintf ("Turn %d is %d at (%d,%d), mid pt is %d@%d, final @%d\n", 01222 segment, turnpoints[segment], 01223 xcoords[turnpoints[segment]], ycoords[turnpoints[segment]], 01224 ptindex - 1, xcoords[ptindex - 1], xstarts[segment]); 01225 } 01226 01227 xstarts[segment] = max_x; 01228 return segment; /*no of splines */ 01229 } 01230 01231 01232 /********************************************************************** 01233 * split_stepped_spline 01234 * 01235 * Re-segment the spline in cases where there is a big step function. 01236 * Return TRUE if any were done. 01237 **********************************************************************/ 01238 01239 BOOL8 01240 split_stepped_spline ( //make xstarts 01241 QSPLINE * baseline, //current shot 01242 float jumplimit, //max step fuction 01243 int xcoords[], /*points to work on */ 01244 int xstarts[], //result 01245 int &segments //no of segments 01246 ) { 01247 BOOL8 doneany; //return value 01248 register int segment; /*partition no */ 01249 int startindex, centreindex, endindex; 01250 float leftcoord, rightcoord; 01251 int leftindex, rightindex; 01252 float step; //spline step 01253 01254 doneany = FALSE; 01255 startindex = 0; 01256 for (segment = 1; segment < segments - 1; segment++) { 01257 step = baseline->step ((xstarts[segment - 1] + xstarts[segment]) / 2.0, 01258 (xstarts[segment] + xstarts[segment + 1]) / 2.0); 01259 if (step < 0) 01260 step = -step; 01261 if (step > jumplimit) { 01262 while (xcoords[startindex] < xstarts[segment - 1]) 01263 startindex++; 01264 centreindex = startindex; 01265 while (xcoords[centreindex] < xstarts[segment]) 01266 centreindex++; 01267 endindex = centreindex; 01268 while (xcoords[endindex] < xstarts[segment + 1]) 01269 endindex++; 01270 if (segments >= SPLINESIZE) { 01271 if (textord_debug_baselines) 01272 tprintf ("Too many segments to resegment spline!!\n"); 01273 } 01274 else if (endindex - startindex >= textord_spline_medianwin * 3) { 01275 while (centreindex - startindex < 01276 textord_spline_medianwin * 3 / 2) 01277 centreindex++; 01278 while (endindex - centreindex < 01279 textord_spline_medianwin * 3 / 2) 01280 centreindex--; 01281 leftindex = (startindex + startindex + centreindex) / 3; 01282 rightindex = (centreindex + endindex + endindex) / 3; 01283 leftcoord = 01284 (xcoords[startindex] * 2 + xcoords[centreindex]) / 3.0; 01285 rightcoord = 01286 (xcoords[centreindex] + xcoords[endindex] * 2) / 3.0; 01287 while (xcoords[leftindex] > leftcoord 01288 && leftindex - startindex > textord_spline_medianwin) 01289 leftindex--; 01290 while (xcoords[leftindex] < leftcoord 01291 && centreindex - leftindex > 01292 textord_spline_medianwin / 2) 01293 leftindex++; 01294 if (xcoords[leftindex] - leftcoord > 01295 leftcoord - xcoords[leftindex - 1]) 01296 leftindex--; 01297 while (xcoords[rightindex] > rightcoord 01298 && rightindex - centreindex > 01299 textord_spline_medianwin / 2) 01300 rightindex--; 01301 while (xcoords[rightindex] < rightcoord 01302 && endindex - rightindex > textord_spline_medianwin) 01303 rightindex++; 01304 if (xcoords[rightindex] - rightcoord > 01305 rightcoord - xcoords[rightindex - 1]) 01306 rightindex--; 01307 if (textord_debug_baselines) 01308 tprintf ("Splitting spline at %d with step %g at (%d,%d)\n", 01309 xstarts[segment], 01310 baseline-> 01311 step ((xstarts[segment - 1] + 01312 xstarts[segment]) / 2.0, 01313 (xstarts[segment] + 01314 xstarts[segment + 1]) / 2.0), 01315 (xcoords[leftindex - 1] + xcoords[leftindex]) / 2, 01316 (xcoords[rightindex - 1] + xcoords[rightindex]) / 2); 01317 insert_spline_point (xstarts, segment, 01318 (xcoords[leftindex - 1] + 01319 xcoords[leftindex]) / 2, 01320 (xcoords[rightindex - 1] + 01321 xcoords[rightindex]) / 2, segments); 01322 doneany = TRUE; 01323 } 01324 else if (textord_debug_baselines) { 01325 tprintf 01326 ("Resegmenting spline failed - insufficient pts (%d,%d,%d,%d)\n", 01327 startindex, centreindex, endindex, 01328 (inT32) textord_spline_medianwin); 01329 } 01330 } 01331 // else tprintf("Spline step at %d is %g\n", 01332 // xstarts[segment], 01333 // baseline->step((xstarts[segment-1]+xstarts[segment])/2.0, 01334 // (xstarts[segment]+xstarts[segment+1])/2.0)); 01335 } 01336 return doneany; 01337 } 01338 01339 01340 /********************************************************************** 01341 * insert_spline_point 01342 * 01343 * Insert a new spline point and shuffle up the others. 01344 **********************************************************************/ 01345 01346 void 01347 insert_spline_point ( //get descenders 01348 int xstarts[], //starts to shuffle 01349 int segment, //insertion pt 01350 int coord1, //coords to add 01351 int coord2, int &segments //total segments 01352 ) { 01353 int index; //for shuffling 01354 01355 for (index = segments; index > segment; index--) 01356 xstarts[index + 1] = xstarts[index]; 01357 segments++; 01358 xstarts[segment] = coord1; 01359 xstarts[segment + 1] = coord2; 01360 } 01361 01362 01363 /********************************************************************** 01364 * find_lesser_parts 01365 * 01366 * Average the step from the spline for the other partitions 01367 * and find the commonest partition which has a descender. 01368 **********************************************************************/ 01369 01370 void 01371 find_lesser_parts ( //get descenders 01372 TO_ROW * row, //row to process 01373 TBOX blobcoords[], //bounding boxes 01374 int blobcount, /*no of blobs */ 01375 char partids[], /*partition of each blob */ 01376 int partsizes[], /*size of each part */ 01377 int partcount, /*no of partitions */ 01378 int bestpart /*biggest partition */ 01379 ) { 01380 register int blobindex; /*index of blob */ 01381 register int partition; /*current partition */ 01382 int xcentre; /*centre of blob */ 01383 int poscount; /*count of best up step */ 01384 int negcount; /*count of best down step */ 01385 float partsteps[MAXPARTS]; /*average step to part */ 01386 float bestneg; /*best down step */ 01387 int runlength; /*length of bad run */ 01388 int biggestrun; /*biggest bad run */ 01389 01390 biggestrun = 0; 01391 for (partition = 0; partition < partcount; partition++) 01392 partsteps[partition] = 0.0; /*zero accumulators */ 01393 for (runlength = 0, blobindex = 0; blobindex < blobcount; blobindex++) { 01394 xcentre = (blobcoords[blobindex].left () 01395 + blobcoords[blobindex].right ()) >> 1; 01396 /*in other parts */ 01397 int part_id = 01398 static_cast<int>(static_cast<unsigned char>(partids[blobindex])); 01399 if (part_id != bestpart) { 01400 runlength++; /*run of non bests */ 01401 if (runlength > biggestrun) 01402 biggestrun = runlength; 01403 partsteps[part_id] += blobcoords[blobindex].bottom() 01404 - row->baseline.y(xcentre); 01405 } 01406 else 01407 runlength = 0; 01408 } 01409 if (biggestrun > MAXBADRUN) 01410 row->xheight = -1.0f; /*failed */ 01411 else 01412 row->xheight = 1.0f; /*success */ 01413 poscount = negcount = 0; 01414 bestneg = 0.0; /*no step yet */ 01415 for (partition = 0; partition < partcount; partition++) { 01416 if (partition != bestpart) { 01417 01418 //by jetsoft divide by zero possible 01419 if (partsizes[partition]==0) 01420 partsteps[partition]=0; 01421 else 01422 partsteps[partition] /= partsizes[partition]; 01423 // 01424 01425 01426 if (partsteps[partition] >= MINASCRISE 01427 && partsizes[partition] > poscount) { 01428 poscount = partsizes[partition]; 01429 } 01430 if (partsteps[partition] <= -MINASCRISE 01431 && partsizes[partition] > negcount) { 01432 /*ascender rise */ 01433 bestneg = partsteps[partition]; 01434 /*2nd most popular */ 01435 negcount = partsizes[partition]; 01436 } 01437 } 01438 } 01439 /*average x-height */ 01440 partsteps[bestpart] /= blobcount; 01441 row->descdrop = bestneg; 01442 } 01443 01444 01445 /********************************************************************** 01446 * old_first_xheight 01447 * 01448 * Makes an x-height spline by copying the baseline and shifting it. 01449 * It estimates the x-height across the line to use as the shift. 01450 * It also finds the ascender height if it can. 01451 **********************************************************************/ 01452 01453 void 01454 old_first_xheight ( //the wiseowl way 01455 TO_ROW * row, /*current row */ 01456 TBOX blobcoords[], /*blob bounding boxes */ 01457 int initialheight, //initial guess 01458 int blobcount, /*blobs in blobcoords */ 01459 QSPLINE * baseline, /*established */ 01460 float jumplimit /*min ascender height */ 01461 ) { 01462 register int blobindex; /*current blob */ 01463 /*height statistics */ 01464 STATS heightstat (0, MAXHEIGHT); 01465 int height; /*height of blob */ 01466 int xcentre; /*centre of blob */ 01467 int lineheight; /*approx xheight */ 01468 float ascenders; /*ascender sum */ 01469 int asccount; /*no of ascenders */ 01470 float xsum; /*xheight sum */ 01471 int xcount; /*xheight count */ 01472 register float diff; /*height difference */ 01473 01474 if (blobcount > 1) { 01475 for (blobindex = 0; blobindex < blobcount; blobindex++) { 01476 xcentre = (blobcoords[blobindex].left () 01477 + blobcoords[blobindex].right ()) / 2; 01478 /*height of blob */ 01479 height = (int) (blobcoords[blobindex].top () - baseline->y (xcentre) + 0.5); 01480 if (height > initialheight * oldbl_xhfract 01481 && height > textord_min_xheight) 01482 heightstat.add (height, 1); 01483 } 01484 if (heightstat.get_total () > 3) { 01485 lineheight = (int) heightstat.ile (0.25); 01486 if (lineheight <= 0) 01487 lineheight = (int) heightstat.ile (0.5); 01488 } 01489 else 01490 lineheight = initialheight; 01491 } 01492 else { 01493 lineheight = (int) (blobcoords[0].top () 01494 - baseline->y ((blobcoords[0].left () 01495 + blobcoords[0].right ()) / 2) + 01496 0.5); 01497 } 01498 01499 xsum = 0.0f; 01500 xcount = 0; 01501 for (ascenders = 0.0f, asccount = 0, blobindex = 0; blobindex < blobcount; 01502 blobindex++) { 01503 xcentre = (blobcoords[blobindex].left () 01504 + blobcoords[blobindex].right ()) / 2; 01505 diff = blobcoords[blobindex].top () - baseline->y (xcentre); 01506 /*is it ascender */ 01507 if (diff > lineheight + jumplimit) { 01508 ascenders += diff; 01509 asccount++; /*count ascenders */ 01510 } 01511 else if (diff > lineheight - jumplimit) { 01512 xsum += diff; /*mean xheight */ 01513 xcount++; 01514 } 01515 } 01516 if (xcount > 0) 01517 xsum /= xcount; /*average xheight */ 01518 else 01519 xsum = (float) lineheight; /*guess it */ 01520 row->xheight *= xsum; 01521 if (asccount > 0) 01522 row->ascrise = ascenders / asccount - xsum; 01523 else 01524 row->ascrise = 0.0f; /*had none */ 01525 if (row->xheight == 0) 01526 row->xheight = -1.0f; 01527 } 01528 01529 01530 /********************************************************************** 01531 * make_first_xheight 01532 * 01533 * Makes an x-height spline by copying the baseline and shifting it. 01534 * It estimates the x-height across the line to use as the shift. 01535 * It also finds the ascender height if it can. 01536 **********************************************************************/ 01537 01538 void 01539 make_first_xheight ( //find xheight 01540 TO_ROW * row, /*current row */ 01541 TBOX blobcoords[], /*blob bounding boxes */ 01542 int lineheight, //initial guess 01543 int init_lineheight, //block level guess 01544 int blobcount, /*blobs in blobcoords */ 01545 QSPLINE * baseline, /*established */ 01546 float jumplimit /*min ascender height */ 01547 ) { 01548 STATS heightstat (0, HEIGHTBUCKETS); 01549 int lefts[HEIGHTBUCKETS]; 01550 int rights[HEIGHTBUCKETS]; 01551 int modelist[MODENUM]; 01552 int blobindex; 01553 int mode_count; //blobs to count in thr 01554 int sign_bit; 01555 int mode_threshold; 01556 const int kBaselineTouch = 2; // This really should change with resolution. 01557 const int kGoodStrength = 8; // Strength of baseline-touching heights. 01558 const float kMinHeight = 0.25; // Min fraction of lineheight to use. 01559 01560 sign_bit = row->xheight > 0 ? 1 : -1; 01561 01562 memset(lefts, 0, HEIGHTBUCKETS * sizeof(lefts[0])); 01563 memset(rights, 0, HEIGHTBUCKETS * sizeof(rights[0])); 01564 mode_count = 0; 01565 for (blobindex = 0; blobindex < blobcount; blobindex++) { 01566 int xcenter = (blobcoords[blobindex].left () + 01567 blobcoords[blobindex].right ()) / 2; 01568 float base = baseline->y(xcenter); 01569 float bottomdiff = fabs(base - blobcoords[blobindex].bottom()); 01570 int strength = textord_ocropus_mode && 01571 bottomdiff <= kBaselineTouch ? kGoodStrength : 1; 01572 int height = static_cast<int>(blobcoords[blobindex].top () - base + 0.5); 01573 if (blobcoords[blobindex].height () > init_lineheight * kMinHeight) { 01574 if (height > lineheight * oldbl_xhfract 01575 && height > textord_min_xheight) { 01576 heightstat.add (height, strength); 01577 if (height < HEIGHTBUCKETS) { 01578 if (xcenter > rights[height]) 01579 rights[height] = xcenter; 01580 if (xcenter > 0 && (lefts[height] == 0 || xcenter < lefts[height])) 01581 lefts[height] = xcenter; 01582 } 01583 } 01584 mode_count += strength; 01585 } 01586 } 01587 01588 mode_threshold = (int) (blobcount * 0.1); 01589 if (oldbl_dot_error_size > 1 || oldbl_xhfix) 01590 mode_threshold = (int) (mode_count * 0.1); 01591 01592 if (textord_oldbl_debug) { 01593 tprintf ("blobcount=%d, mode_count=%d, mode_t=%d\n", 01594 blobcount, mode_count, mode_threshold); 01595 } 01596 find_top_modes(&heightstat, HEIGHTBUCKETS, modelist, MODENUM); 01597 if (textord_oldbl_debug) { 01598 for (blobindex = 0; blobindex < MODENUM; blobindex++) 01599 tprintf ("mode[%d]=%d ", blobindex, modelist[blobindex]); 01600 tprintf ("\n"); 01601 } 01602 pick_x_height(row, modelist, lefts, rights, &heightstat, mode_threshold); 01603 01604 if (textord_oldbl_debug) 01605 tprintf ("Output xheight=%g\n", row->xheight); 01606 if (row->xheight < 0 && textord_oldbl_debug) 01607 tprintf ("warning: Row Line height < 0; %4.2f\n", row->xheight); 01608 01609 if (sign_bit < 0) 01610 row->xheight = -row->xheight; 01611 } 01612 01613 /********************************************************************** 01614 * find_top_modes 01615 * 01616 * Fill the input array with the indices of the top ten modes of the 01617 * input distribution. 01618 **********************************************************************/ 01619 01620 const int kMinModeFactorOcropus = 32; 01621 const int kMinModeFactor = 12; 01622 01623 void 01624 find_top_modes ( //get modes 01625 STATS * stats, //stats to hack 01626 int statnum, //no of piles 01627 int modelist[], int modenum //no of modes to get 01628 ) { 01629 int mode_count; 01630 int last_i = 0; 01631 int last_max = MAX_INT32; 01632 int i; 01633 int mode; 01634 int total_max = 0; 01635 int mode_factor = textord_ocropus_mode ? 01636 kMinModeFactorOcropus : kMinModeFactor; 01637 01638 for (mode_count = 0; mode_count < modenum; mode_count++) { 01639 mode = 0; 01640 for (i = 0; i < statnum; i++) { 01641 if (stats->pile_count (i) > stats->pile_count (mode)) { 01642 if ((stats->pile_count (i) < last_max) || 01643 ((stats->pile_count (i) == last_max) && (i > last_i))) { 01644 mode = i; 01645 } 01646 } 01647 } 01648 last_i = mode; 01649 last_max = stats->pile_count (last_i); 01650 total_max += last_max; 01651 if (last_max <= total_max / mode_factor) 01652 mode = 0; 01653 modelist[mode_count] = mode; 01654 } 01655 } 01656 01657 01658 /********************************************************************** 01659 * pick_x_height 01660 * 01661 * Choose based on the height modes the best x height value. 01662 **********************************************************************/ 01663 01664 void pick_x_height(TO_ROW * row, //row to do 01665 int modelist[], 01666 int lefts[], int rights[], 01667 STATS * heightstat, 01668 int mode_threshold) { 01669 int x; 01670 int y; 01671 int z; 01672 float ratio; 01673 int found_one_bigger = FALSE; 01674 int best_x_height = 0; 01675 int best_asc = 0; 01676 int num_in_best; 01677 01678 for (x = 0; x < MODENUM; x++) { 01679 for (y = 0; y < MODENUM; y++) { 01680 /* Check for two modes */ 01681 if (modelist[x] && modelist[y] && 01682 heightstat->pile_count (modelist[x]) > mode_threshold && 01683 (!textord_ocropus_mode || 01684 MIN(rights[modelist[x]], rights[modelist[y]]) > 01685 MAX(lefts[modelist[x]], lefts[modelist[y]]))) { 01686 ratio = (float) modelist[y] / (float) modelist[x]; 01687 if (1.2 < ratio && ratio < 1.8) { 01688 /* Two modes found */ 01689 best_x_height = modelist[x]; 01690 num_in_best = heightstat->pile_count (modelist[x]); 01691 01692 /* Try to get one higher */ 01693 do { 01694 found_one_bigger = FALSE; 01695 for (z = 0; z < MODENUM; z++) { 01696 if (modelist[z] == best_x_height + 1 && 01697 (!textord_ocropus_mode || 01698 MIN(rights[modelist[x]], rights[modelist[y]]) > 01699 MAX(lefts[modelist[x]], lefts[modelist[y]]))) { 01700 ratio = (float) modelist[y] / (float) modelist[z]; 01701 if ((1.2 < ratio && ratio < 1.8) && 01702 /* Should be half of best */ 01703 heightstat->pile_count (modelist[z]) > 01704 num_in_best * 0.5) { 01705 best_x_height++; 01706 found_one_bigger = TRUE; 01707 break; 01708 } 01709 } 01710 } 01711 } 01712 while (found_one_bigger); 01713 01714 /* try to get a higher ascender */ 01715 01716 best_asc = modelist[y]; 01717 num_in_best = heightstat->pile_count (modelist[y]); 01718 01719 /* Try to get one higher */ 01720 do { 01721 found_one_bigger = FALSE; 01722 for (z = 0; z < MODENUM; z++) { 01723 if (modelist[z] > best_asc && 01724 (!textord_ocropus_mode || 01725 MIN(rights[modelist[x]], rights[modelist[y]]) > 01726 MAX(lefts[modelist[x]], lefts[modelist[y]]))) { 01727 ratio = (float) modelist[z] / (float) best_x_height; 01728 if ((1.2 < ratio && ratio < 1.8) && 01729 /* Should be half of best */ 01730 heightstat->pile_count (modelist[z]) > 01731 num_in_best * 0.5) { 01732 best_asc = modelist[z]; 01733 found_one_bigger = TRUE; 01734 break; 01735 } 01736 } 01737 } 01738 } 01739 while (found_one_bigger); 01740 01741 row->xheight = (float) best_x_height; 01742 row->ascrise = (float) best_asc - best_x_height; 01743 return; 01744 } 01745 } 01746 } 01747 } 01748 01749 best_x_height = modelist[0]; /* Single Mode found */ 01750 num_in_best = heightstat->pile_count (best_x_height); 01751 do { 01752 /* Try to get one higher */ 01753 found_one_bigger = FALSE; 01754 for (z = 1; z < MODENUM; z++) { 01755 /* Should be half of best */ 01756 if ((modelist[z] == best_x_height + 1) && 01757 (heightstat->pile_count (modelist[z]) > num_in_best * 0.5)) { 01758 best_x_height++; 01759 found_one_bigger = TRUE; 01760 break; 01761 } 01762 } 01763 } 01764 while (found_one_bigger); 01765 01766 row->ascrise = 0.0f; 01767 row->xheight = (float) best_x_height; 01768 if (row->xheight == 0) 01769 row->xheight = -1.0f; 01770 }