tesseract  3.03
/usr/local/google/home/jbreiden/tesseract-ocr-read-only/textord/oldbasel.cpp
Go to the documentation of this file.
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 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines