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