tesseract  3.03
/usr/local/google/home/jbreiden/tesseract-ocr-read-only/textord/pitsync1.cpp
Go to the documentation of this file.
00001 /**********************************************************************
00002  * File:        pitsync1.cpp  (Formerly pitsync.c)
00003  * Description: Code to find the optimum fixed pitch segmentation of some blobs.
00004  * Author:              Ray Smith
00005  * Created:             Thu Nov 19 11:48:05 GMT 1992
00006  *
00007  * (C) Copyright 1992, 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 #ifdef __UNIX__
00021 #include          <assert.h>
00022 #endif
00023 #include          <math.h>
00024 #include          "memry.h"
00025 #include          "pitsync1.h"
00026 
00027 ELISTIZE (FPSEGPT) CLISTIZE (FPSEGPT_LIST)
00028 #define EXTERN
00029 EXTERN
00030 INT_VAR (pitsync_linear_version, 6, "Use new fast algorithm");
00031 EXTERN
00032 double_VAR (pitsync_joined_edge, 0.75,
00033 "Dist inside big blob for chopping");
00034 EXTERN
00035 double_VAR (pitsync_offset_freecut_fraction, 0.25,
00036 "Fraction of cut for free cuts");
00037 EXTERN
00038 INT_VAR (pitsync_fake_depth, 1, "Max advance fake generation");
00039 
00040 /**********************************************************************
00041  * FPSEGPT::FPSEGPT
00042  *
00043  * Constructor to make a new FPSEGPT.
00044  * The existing FPCUTPT is duplicated.
00045  **********************************************************************/
00046 
00047 FPSEGPT::FPSEGPT(                //constructor
00048                  FPCUTPT *cutpt  //create from new form
00049                 ) {
00050   pred = NULL;
00051   mean_sum = cutpt->sum ();
00052   sq_sum = cutpt->squares ();
00053   cost = cutpt->cost_function ();
00054   faked = cutpt->faked;
00055   terminal = cutpt->terminal;
00056   fake_count = cutpt->fake_count;
00057   xpos = cutpt->position ();
00058   mid_cuts = cutpt->cheap_cuts ();
00059 }
00060 
00061 
00062 /**********************************************************************
00063  * FPSEGPT::FPSEGPT
00064  *
00065  * Constructor to make a new FPSEGPT.
00066  **********************************************************************/
00067 
00068 FPSEGPT::FPSEGPT (               //constructor
00069 inT16 x                          //position
00070 ):xpos (x) {
00071   pred = NULL;
00072   mean_sum = 0;
00073   sq_sum = 0;
00074   cost = 0;
00075   faked = FALSE;
00076   terminal = FALSE;
00077   fake_count = 0;
00078   mid_cuts = 0;
00079 }
00080 
00081 
00082 /**********************************************************************
00083  * FPSEGPT::FPSEGPT
00084  *
00085  * Constructor to make a new FPSEGPT.
00086  **********************************************************************/
00087 
00088 FPSEGPT::FPSEGPT (               //constructor
00089 inT16 x,                         //position
00090 BOOL8 faking,                    //faking this one
00091 inT16 offset,                    //dist to gap
00092 inT16 region_index,              //segment number
00093 inT16 pitch,                     //proposed pitch
00094 inT16 pitch_error,               //allowed tolerance
00095 FPSEGPT_LIST * prev_list         //previous segment
00096 ):xpos (x) {
00097   inT16 best_fake;               //on previous
00098   FPSEGPT *segpt;                //segment point
00099   inT32 dist;                    //from prev segment
00100   double sq_dist;                //squared distance
00101   double mean;                   //mean pitch
00102   double total;                  //total dists
00103   double factor;                 //cost function
00104   FPSEGPT_IT pred_it = prev_list;//for previuos segment
00105 
00106   cost = MAX_FLOAT32;
00107   pred = NULL;
00108   faked = faking;
00109   terminal = FALSE;
00110   best_fake = MAX_INT16;
00111   mid_cuts = 0;
00112   for (pred_it.mark_cycle_pt (); !pred_it.cycled_list (); pred_it.forward ()) {
00113     segpt = pred_it.data ();
00114     if (segpt->fake_count < best_fake)
00115       best_fake = segpt->fake_count;
00116     dist = x - segpt->xpos;
00117     if (dist >= pitch - pitch_error && dist <= pitch + pitch_error
00118     && !segpt->terminal) {
00119       total = segpt->mean_sum + dist;
00120       sq_dist = dist * dist + segpt->sq_sum + offset * offset;
00121       //sum of squarees
00122       mean = total / region_index;
00123       factor = mean - pitch;
00124       factor *= factor;
00125       factor += sq_dist / (region_index) - mean * mean;
00126       if (factor < cost) {
00127         cost = factor;           //find least cost
00128         pred = segpt;            //save path
00129         mean_sum = total;
00130         sq_sum = sq_dist;
00131         fake_count = segpt->fake_count + faked;
00132       }
00133     }
00134   }
00135   if (fake_count > best_fake + 1)
00136     pred = NULL;                 //fail it
00137 }
00138 
00139 
00140 /**********************************************************************
00141  * check_pitch_sync
00142  *
00143  * Construct the lattice of possible segmentation points and choose the
00144  * optimal path. Return the optimal path only.
00145  * The return value is a measure of goodness of the sync.
00146  **********************************************************************/
00147 
00148 double check_pitch_sync(                        //find segmentation
00149                         BLOBNBOX_IT *blob_it,   //blobs to do
00150                         inT16 blob_count,       //no of blobs
00151                         inT16 pitch,            //pitch estimate
00152                         inT16 pitch_error,      //tolerance
00153                         STATS *projection,      //vertical
00154                         FPSEGPT_LIST *seg_list  //output list
00155                        ) {
00156   inT16 x;                       //current coord
00157   inT16 min_index;               //blob number
00158   inT16 max_index;               //blob number
00159   inT16 left_edge;               //of word
00160   inT16 right_edge;              //of word
00161   inT16 right_max;               //max allowed x
00162   inT16 min_x;                   //in this region
00163   inT16 max_x;
00164   inT16 region_index;
00165   inT16 best_region_index = 0;   //for best result
00166   inT16 offset;                  //dist to legal area
00167   inT16 left_best_x;             //edge of good region
00168   inT16 right_best_x;            //right edge
00169   TBOX min_box;                   //bounding box
00170   TBOX max_box;                   //bounding box
00171   TBOX next_box;                  //box of next blob
00172   FPSEGPT *segpt;                //segment point
00173   FPSEGPT_LIST *segpts;          //points in a segment
00174   double best_cost;              //best path
00175   double mean_sum;               //computes result
00176   FPSEGPT *best_end;             //end of best path
00177   BLOBNBOX_IT min_it;            //copy iterator
00178   BLOBNBOX_IT max_it;            //copy iterator
00179   FPSEGPT_IT segpt_it;           //iterator
00180                                  //output segments
00181   FPSEGPT_IT outseg_it = seg_list;
00182   FPSEGPT_LIST_CLIST lattice;    //list of lists
00183                                  //region iterator
00184   FPSEGPT_LIST_C_IT lattice_it = &lattice;
00185 
00186   //      tprintf("Computing sync on word of %d blobs with pitch %d\n",
00187   //              blob_count, pitch);
00188   //      if (blob_count==8 && pitch==27)
00189   //              projection->print(stdout,TRUE);
00190   if (pitch < 3)
00191     pitch = 3;                   //nothing ludicrous
00192   if ((pitch - 3) / 2 < pitch_error)
00193     pitch_error = (pitch - 3) / 2;
00194   min_it = *blob_it;
00195   min_box = box_next (&min_it);  //get box
00196   //      if (blob_count==8 && pitch==27)
00197   //              tprintf("1st box at (%d,%d)->(%d,%d)\n",
00198   //                      min_box.left(),min_box.bottom(),
00199   //                      min_box.right(),min_box.top());
00200                                  //left of word
00201   left_edge = min_box.left () + pitch_error;
00202   for (min_index = 1; min_index < blob_count; min_index++) {
00203     min_box = box_next (&min_it);
00204     //              if (blob_count==8 && pitch==27)
00205     //                      tprintf("Box at (%d,%d)->(%d,%d)\n",
00206     //                              min_box.left(),min_box.bottom(),
00207     //                              min_box.right(),min_box.top());
00208   }
00209   right_edge = min_box.right (); //end of word
00210   max_x = left_edge;
00211                                  //min permissible
00212   min_x = max_x - pitch + pitch_error * 2 + 1;
00213   right_max = right_edge + pitch - pitch_error - 1;
00214   segpts = new FPSEGPT_LIST;     //list of points
00215   segpt_it.set_to_list (segpts);
00216   for (x = min_x; x <= max_x; x++) {
00217     segpt = new FPSEGPT (x);     //make a new one
00218                                  //put in list
00219     segpt_it.add_after_then_move (segpt);
00220   }
00221                                  //first segment
00222   lattice_it.add_before_then_move (segpts);
00223   min_index = 0;
00224   region_index = 1;
00225   best_cost = MAX_FLOAT32;
00226   best_end = NULL;
00227   min_it = *blob_it;
00228   min_box = box_next (&min_it);  //first box
00229   do {
00230     left_best_x = -1;
00231     right_best_x = -1;
00232     segpts = new FPSEGPT_LIST;   //list of points
00233     segpt_it.set_to_list (segpts);
00234     min_x += pitch - pitch_error;//next limits
00235     max_x += pitch + pitch_error;
00236     while (min_box.right () < min_x && min_index < blob_count) {
00237       min_index++;
00238       min_box = box_next (&min_it);
00239     }
00240     max_it = min_it;
00241     max_index = min_index;
00242     max_box = min_box;
00243     next_box = box_next (&max_it);
00244     for (x = min_x; x <= max_x && x <= right_max; x++) {
00245       while (x < right_edge && max_index < blob_count
00246       && x > max_box.right ()) {
00247         max_index++;
00248         max_box = next_box;
00249         next_box = box_next (&max_it);
00250       }
00251       if (x <= max_box.left () + pitch_error
00252         || x >= max_box.right () - pitch_error || x >= right_edge
00253         || (max_index < blob_count - 1 && x >= next_box.left ())
00254         || (x - max_box.left () > pitch * pitsync_joined_edge
00255       && max_box.right () - x > pitch * pitsync_joined_edge)) {
00256       //                      || projection->local_min(x))
00257         if (x - max_box.left () > 0
00258           && x - max_box.left () <= pitch_error)
00259                                  //dist to real break
00260           offset = x - max_box.left ();
00261         else if (max_box.right () - x > 0
00262           && max_box.right () - x <= pitch_error
00263           && (max_index >= blob_count - 1
00264           || x < next_box.left ()))
00265           offset = max_box.right () - x;
00266         else
00267           offset = 0;
00268         //                              offset=pitsync_offset_freecut_fraction*projection->pile_count(x);
00269         segpt = new FPSEGPT (x, FALSE, offset, region_index,
00270           pitch, pitch_error, lattice_it.data ());
00271       }
00272       else {
00273         offset = projection->pile_count (x);
00274         segpt = new FPSEGPT (x, TRUE, offset, region_index,
00275           pitch, pitch_error, lattice_it.data ());
00276       }
00277       if (segpt->previous () != NULL) {
00278         segpt_it.add_after_then_move (segpt);
00279         if (x >= right_edge - pitch_error) {
00280           segpt->terminal = TRUE;//no more wanted
00281           if (segpt->cost_function () < best_cost) {
00282             best_cost = segpt->cost_function ();
00283             //find least
00284             best_end = segpt;
00285             best_region_index = region_index;
00286             left_best_x = x;
00287             right_best_x = x;
00288           }
00289           else if (segpt->cost_function () == best_cost
00290             && right_best_x == x - 1)
00291             right_best_x = x;
00292         }
00293       }
00294       else {
00295         delete segpt;            //no good
00296       }
00297     }
00298     if (segpts->empty ()) {
00299       if (best_end != NULL)
00300         break;                   //already found one
00301       make_illegal_segment (lattice_it.data (), min_box, min_it,
00302         region_index, pitch, pitch_error, segpts);
00303     }
00304     else {
00305       if (right_best_x > left_best_x + 1) {
00306         left_best_x = (left_best_x + right_best_x + 1) / 2;
00307         for (segpt_it.mark_cycle_pt (); !segpt_it.cycled_list ()
00308           && segpt_it.data ()->position () != left_best_x;
00309           segpt_it.forward ());
00310         if (segpt_it.data ()->position () == left_best_x)
00311                                  //middle of region
00312           best_end = segpt_it.data ();
00313       }
00314     }
00315                                  //new segment
00316     lattice_it.add_before_then_move (segpts);
00317     region_index++;
00318   }
00319   while (min_x < right_edge);
00320   ASSERT_HOST (best_end != NULL);//must always find some
00321 
00322   for (lattice_it.mark_cycle_pt (); !lattice_it.cycled_list ();
00323   lattice_it.forward ()) {
00324     segpts = lattice_it.data ();
00325     segpt_it.set_to_list (segpts);
00326     //              if (blob_count==8 && pitch==27)
00327     //              {
00328     //                      for (segpt_it.mark_cycle_pt();!segpt_it.cycled_list();segpt_it.forward())
00329     //                      {
00330     //                              segpt=segpt_it.data();
00331     //                              tprintf("At %d, (%x) cost=%g, m=%g, sq=%g, pred=%x\n",
00332     //                                      segpt->position(),segpt,segpt->cost_function(),
00333     //                                      segpt->sum(),segpt->squares(),segpt->previous());
00334     //                      }
00335     //                      tprintf("\n");
00336     //              }
00337     for (segpt_it.mark_cycle_pt (); !segpt_it.cycled_list ()
00338       && segpt_it.data () != best_end; segpt_it.forward ());
00339     if (segpt_it.data () == best_end) {
00340                                  //save good one
00341       segpt = segpt_it.extract ();
00342       outseg_it.add_before_then_move (segpt);
00343       best_end = segpt->previous ();
00344     }
00345   }
00346   ASSERT_HOST (best_end == NULL);
00347   ASSERT_HOST (!outseg_it.empty ());
00348   outseg_it.move_to_last ();
00349   mean_sum = outseg_it.data ()->sum ();
00350   mean_sum = mean_sum * mean_sum / best_region_index;
00351   if (outseg_it.data ()->squares () - mean_sum < 0)
00352     tprintf ("Impossible sqsum=%g, mean=%g, total=%d\n",
00353       outseg_it.data ()->squares (), outseg_it.data ()->sum (),
00354       best_region_index);
00355   lattice.deep_clear ();         //shift the lot
00356   return outseg_it.data ()->squares () - mean_sum;
00357 }
00358 
00359 
00360 /**********************************************************************
00361  * make_illegal_segment
00362  *
00363  * Make a fake set of chop points due to having no legal places.
00364  **********************************************************************/
00365 
00366 void make_illegal_segment(                          //find segmentation
00367                           FPSEGPT_LIST *prev_list,  //previous segments
00368                           TBOX blob_box,             //bounding box
00369                           BLOBNBOX_IT blob_it,      //iterator
00370                           inT16 region_index,       //number of segment
00371                           inT16 pitch,              //pitch estimate
00372                           inT16 pitch_error,        //tolerance
00373                           FPSEGPT_LIST *seg_list    //output list
00374                          ) {
00375   inT16 x;                       //current coord
00376   inT16 min_x = 0;               //in this region
00377   inT16 max_x = 0;
00378   inT16 offset;                  //dist to edge
00379   FPSEGPT *segpt;                //segment point
00380   FPSEGPT *prevpt;               //previous point
00381   float best_cost;               //best path
00382   FPSEGPT_IT segpt_it = seg_list;//iterator
00383                                  //previous points
00384   FPSEGPT_IT prevpt_it = prev_list;
00385 
00386   best_cost = MAX_FLOAT32;
00387   for (prevpt_it.mark_cycle_pt (); !prevpt_it.cycled_list ();
00388   prevpt_it.forward ()) {
00389     prevpt = prevpt_it.data ();
00390     if (prevpt->cost_function () < best_cost) {
00391                                  //find least
00392       best_cost = prevpt->cost_function ();
00393       min_x = prevpt->position ();
00394       max_x = min_x;             //limits on coords
00395     }
00396     else if (prevpt->cost_function () == best_cost) {
00397       max_x = prevpt->position ();
00398     }
00399   }
00400   min_x += pitch - pitch_error;
00401   max_x += pitch + pitch_error;
00402   for (x = min_x; x <= max_x; x++) {
00403     while (x > blob_box.right ()) {
00404       blob_box = box_next (&blob_it);
00405     }
00406     offset = x - blob_box.left ();
00407     if (blob_box.right () - x < offset)
00408       offset = blob_box.right () - x;
00409     segpt = new FPSEGPT (x, FALSE, offset,
00410       region_index, pitch, pitch_error, prev_list);
00411     if (segpt->previous () != NULL) {
00412       ASSERT_HOST (offset >= 0);
00413       fprintf (stderr, "made fake at %d\n", x);
00414                                  //make one up
00415       segpt_it.add_after_then_move (segpt);
00416       segpt->faked = TRUE;
00417       segpt->fake_count++;
00418     }
00419     else
00420       delete segpt;
00421   }
00422 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines