Tesseract  3.02
tesseract-ocr/textord/pithsync.cpp
Go to the documentation of this file.
00001 /**********************************************************************
00002  * File:        pithsync.cpp  (Formerly pitsync2.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 #include "mfcpch.h"
00021 #ifdef __UNIX__
00022 #include          <assert.h>
00023 #endif
00024 #include          <math.h>
00025 #include          "memry.h"
00026 #include          "makerow.h"
00027 #include          "pitsync1.h"
00028 #include          "topitch.h"
00029 #include          "pithsync.h"
00030 #include          "tprintf.h"
00031 
00032 #define PROJECTION_MARGIN 10     //arbitrary
00033 
00034 #define EXTERN
00035 
00036 /**********************************************************************
00037  * FPCUTPT::setup
00038  *
00039  * Constructor to make a new FPCUTPT.
00040  **********************************************************************/
00041 
00042 void FPCUTPT::setup(                     //constructor
00043                     FPCUTPT *cutpts,     //predecessors
00044                     inT16 array_origin,  //start coord
00045                     STATS *projection,   //vertical occupation
00046                     inT16 zero_count,    //official zero
00047                     inT16 pitch,         //proposed pitch
00048                     inT16 x,             //position
00049                     inT16 offset         //dist to gap
00050                    ) {
00051                                  //half of pitch
00052   inT16 half_pitch = pitch / 2 - 1;
00053   uinT32 lead_flag;              //new flag
00054   inT32 ind;                     //current position
00055 
00056   if (half_pitch > 31)
00057     half_pitch = 31;
00058   else if (half_pitch < 0)
00059     half_pitch = 0;
00060   lead_flag = 1 << half_pitch;
00061 
00062   pred = NULL;
00063   mean_sum = 0;
00064   sq_sum = offset * offset;
00065   cost = sq_sum;
00066   faked = FALSE;
00067   terminal = FALSE;
00068   fake_count = 0;
00069   xpos = x;
00070   region_index = 0;
00071   mid_cuts = 0;
00072   if (x == array_origin) {
00073     back_balance = 0;
00074     fwd_balance = 0;
00075     for (ind = 0; ind <= half_pitch; ind++) {
00076       fwd_balance >>= 1;
00077       if (projection->pile_count (ind) > zero_count)
00078         fwd_balance |= lead_flag;
00079     }
00080   }
00081   else {
00082     back_balance = cutpts[x - 1 - array_origin].back_balance << 1;
00083     back_balance &= lead_flag + lead_flag - 1;
00084     if (projection->pile_count (x) > zero_count)
00085       back_balance |= 1;
00086     fwd_balance = cutpts[x - 1 - array_origin].fwd_balance >> 1;
00087     if (projection->pile_count (x + half_pitch) > zero_count)
00088       fwd_balance |= lead_flag;
00089   }
00090 }
00091 
00092 
00093 /**********************************************************************
00094  * FPCUTPT::assign
00095  *
00096  * Constructor to make a new FPCUTPT.
00097  **********************************************************************/
00098 
00099 void FPCUTPT::assign(                         //constructor
00100                      FPCUTPT *cutpts,         //predecessors
00101                      inT16 array_origin,      //start coord
00102                      inT16 x,                 //position
00103                      BOOL8 faking,            //faking this one
00104                      BOOL8 mid_cut,           //cheap cut.
00105                      inT16 offset,            //dist to gap
00106                      STATS *projection,       //vertical occupation
00107                      float projection_scale,  //scaling
00108                      inT16 zero_count,        //official zero
00109                      inT16 pitch,             //proposed pitch
00110                      inT16 pitch_error        //allowed tolerance
00111                     ) {
00112   int index;                     //test index
00113   int balance_index;             //for balance factor
00114   inT16 balance_count;           //ding factor
00115   inT16 r_index;                 //test cut number
00116   FPCUTPT *segpt;                //segment point
00117   inT32 dist;                    //from prev segment
00118   double sq_dist;                //squared distance
00119   double mean;                   //mean pitch
00120   double total;                  //total dists
00121   double factor;                 //cost function
00122                                  //half of pitch
00123   inT16 half_pitch = pitch / 2 - 1;
00124   uinT32 lead_flag;              //new flag
00125 
00126   if (half_pitch > 31)
00127     half_pitch = 31;
00128   else if (half_pitch < 0)
00129     half_pitch = 0;
00130   lead_flag = 1 << half_pitch;
00131 
00132   back_balance = cutpts[x - 1 - array_origin].back_balance << 1;
00133   back_balance &= lead_flag + lead_flag - 1;
00134   if (projection->pile_count (x) > zero_count)
00135     back_balance |= 1;
00136   fwd_balance = cutpts[x - 1 - array_origin].fwd_balance >> 1;
00137   if (projection->pile_count (x + half_pitch) > zero_count)
00138     fwd_balance |= lead_flag;
00139 
00140   xpos = x;
00141   cost = MAX_FLOAT32;
00142   pred = NULL;
00143   faked = faking;
00144   terminal = FALSE;
00145   region_index = 0;
00146   fake_count = MAX_INT16;
00147   for (index = x - pitch - pitch_error; index <= x - pitch + pitch_error;
00148   index++) {
00149     if (index >= array_origin) {
00150       segpt = &cutpts[index - array_origin];
00151       dist = x - segpt->xpos;
00152       if (!segpt->terminal && segpt->fake_count < MAX_INT16) {
00153         balance_count = 0;
00154         if (textord_balance_factor > 0) {
00155           if (textord_fast_pitch_test) {
00156             lead_flag = back_balance ^ segpt->fwd_balance;
00157             balance_count = 0;
00158             while (lead_flag != 0) {
00159               balance_count++;
00160               lead_flag &= lead_flag - 1;
00161             }
00162           }
00163           else {
00164             for (balance_index = 0;
00165               index + balance_index < x - balance_index;
00166               balance_index++)
00167             balance_count +=
00168                 (projection->pile_count (index + balance_index) <=
00169                 zero_count) ^ (projection->pile_count (x -
00170                 balance_index)
00171                 <= zero_count);
00172           }
00173           balance_count =
00174             (inT16) (balance_count * textord_balance_factor /
00175             projection_scale);
00176         }
00177         r_index = segpt->region_index + 1;
00178         total = segpt->mean_sum + dist;
00179         balance_count += offset;
00180         sq_dist =
00181           dist * dist + segpt->sq_sum + balance_count * balance_count;
00182         mean = total / r_index;
00183         factor = mean - pitch;
00184         factor *= factor;
00185         factor += sq_dist / (r_index) - mean * mean;
00186         if (factor < cost && segpt->fake_count + faked <= fake_count) {
00187           cost = factor;         //find least cost
00188           pred = segpt;          //save path
00189           mean_sum = total;
00190           sq_sum = sq_dist;
00191           fake_count = segpt->fake_count + faked;
00192           mid_cuts = segpt->mid_cuts + mid_cut;
00193           region_index = r_index;
00194         }
00195       }
00196     }
00197   }
00198 }
00199 
00200 
00201 /**********************************************************************
00202  * FPCUTPT::assign_cheap
00203  *
00204  * Constructor to make a new FPCUTPT on the cheap.
00205  **********************************************************************/
00206 
00207 void FPCUTPT::assign_cheap(                         //constructor
00208                            FPCUTPT *cutpts,         //predecessors
00209                            inT16 array_origin,      //start coord
00210                            inT16 x,                 //position
00211                            BOOL8 faking,            //faking this one
00212                            BOOL8 mid_cut,           //cheap cut.
00213                            inT16 offset,            //dist to gap
00214                            STATS *projection,       //vertical occupation
00215                            float projection_scale,  //scaling
00216                            inT16 zero_count,        //official zero
00217                            inT16 pitch,             //proposed pitch
00218                            inT16 pitch_error        //allowed tolerance
00219                           ) {
00220   int index;                     //test index
00221   inT16 balance_count;           //ding factor
00222   inT16 r_index;                 //test cut number
00223   FPCUTPT *segpt;                //segment point
00224   inT32 dist;                    //from prev segment
00225   double sq_dist;                //squared distance
00226   double mean;                   //mean pitch
00227   double total;                  //total dists
00228   double factor;                 //cost function
00229                                  //half of pitch
00230   inT16 half_pitch = pitch / 2 - 1;
00231   uinT32 lead_flag;              //new flag
00232 
00233   if (half_pitch > 31)
00234     half_pitch = 31;
00235   else if (half_pitch < 0)
00236     half_pitch = 0;
00237   lead_flag = 1 << half_pitch;
00238 
00239   back_balance = cutpts[x - 1 - array_origin].back_balance << 1;
00240   back_balance &= lead_flag + lead_flag - 1;
00241   if (projection->pile_count (x) > zero_count)
00242     back_balance |= 1;
00243   fwd_balance = cutpts[x - 1 - array_origin].fwd_balance >> 1;
00244   if (projection->pile_count (x + half_pitch) > zero_count)
00245     fwd_balance |= lead_flag;
00246 
00247   xpos = x;
00248   cost = MAX_FLOAT32;
00249   pred = NULL;
00250   faked = faking;
00251   terminal = FALSE;
00252   region_index = 0;
00253   fake_count = MAX_INT16;
00254   index = x - pitch;
00255   if (index >= array_origin) {
00256     segpt = &cutpts[index - array_origin];
00257     dist = x - segpt->xpos;
00258     if (!segpt->terminal && segpt->fake_count < MAX_INT16) {
00259       balance_count = 0;
00260       if (textord_balance_factor > 0) {
00261         lead_flag = back_balance ^ segpt->fwd_balance;
00262         balance_count = 0;
00263         while (lead_flag != 0) {
00264           balance_count++;
00265           lead_flag &= lead_flag - 1;
00266         }
00267         balance_count = (inT16) (balance_count * textord_balance_factor
00268           / projection_scale);
00269       }
00270       r_index = segpt->region_index + 1;
00271       total = segpt->mean_sum + dist;
00272       balance_count += offset;
00273       sq_dist =
00274         dist * dist + segpt->sq_sum + balance_count * balance_count;
00275       mean = total / r_index;
00276       factor = mean - pitch;
00277       factor *= factor;
00278       factor += sq_dist / (r_index) - mean * mean;
00279       cost = factor;             //find least cost
00280       pred = segpt;              //save path
00281       mean_sum = total;
00282       sq_sum = sq_dist;
00283       fake_count = segpt->fake_count + faked;
00284       mid_cuts = segpt->mid_cuts + mid_cut;
00285       region_index = r_index;
00286     }
00287   }
00288 }
00289 
00290 
00291 /**********************************************************************
00292  * check_pitch_sync
00293  *
00294  * Construct the lattice of possible segmentation points and choose the
00295  * optimal path. Return the optimal path only.
00296  * The return value is a measure of goodness of the sync.
00297  **********************************************************************/
00298 
00299 double check_pitch_sync2(                          //find segmentation
00300                          BLOBNBOX_IT *blob_it,     //blobs to do
00301                          inT16 blob_count,         //no of blobs
00302                          inT16 pitch,              //pitch estimate
00303                          inT16 pitch_error,        //tolerance
00304                          STATS *projection,        //vertical
00305                          inT16 projection_left,    //edges //scale factor
00306                          inT16 projection_right,
00307                          float projection_scale,
00308                          inT16 &occupation_count,  //no of occupied cells
00309                          FPSEGPT_LIST *seg_list,   //output list
00310                          inT16 start,              //start of good range
00311                          inT16 end                 //end of good range
00312                         ) {
00313   BOOL8 faking;                  //illegal cut pt
00314   BOOL8 mid_cut;                 //cheap cut pt.
00315   inT16 x;                       //current coord
00316   inT16 blob_index;              //blob number
00317   inT16 left_edge;               //of word
00318   inT16 right_edge;              //of word
00319   inT16 array_origin;            //x coord of array
00320   inT16 offset;                  //dist to legal area
00321   inT16 zero_count;              //projection zero
00322   inT16 best_left_x = 0;         //for equals
00323   inT16 best_right_x = 0;        //right edge
00324   TBOX this_box;                  //bounding box
00325   TBOX next_box;                  //box of next blob
00326   FPSEGPT *segpt;                //segment point
00327   FPCUTPT *cutpts;               //array of points
00328   double best_cost;              //best path
00329   double mean_sum;               //computes result
00330   FPCUTPT *best_end;             //end of best path
00331   inT16 best_fake;               //best fake level
00332   inT16 best_count;              //no of cuts
00333   BLOBNBOX_IT this_it;           //copy iterator
00334   FPSEGPT_IT seg_it = seg_list;  //output iterator
00335 
00336   //      tprintf("Computing sync on word of %d blobs with pitch %d\n",
00337   //              blob_count, pitch);
00338   //      if (blob_count==8 && pitch==27)
00339   //              projection->print(stdout,TRUE);
00340   zero_count = 0;
00341   if (pitch < 3)
00342     pitch = 3;                   //nothing ludicrous
00343   if ((pitch - 3) / 2 < pitch_error)
00344     pitch_error = (pitch - 3) / 2;
00345   this_it = *blob_it;
00346   this_box = box_next (&this_it);//get box
00347   //      left_edge=this_box.left();                                              //left of word
00348   //      right_edge=this_box.right();
00349   //      for (blob_index=1;blob_index<blob_count;blob_index++)
00350   //      {
00351   //              this_box=box_next(&this_it);
00352   //              if (this_box.right()>right_edge)
00353   //                      right_edge=this_box.right();
00354   //      }
00355   for (left_edge = projection_left; projection->pile_count (left_edge) == 0
00356     && left_edge < projection_right; left_edge++);
00357   for (right_edge = projection_right; projection->pile_count (right_edge) == 0
00358     && right_edge > left_edge; right_edge--);
00359   ASSERT_HOST (right_edge >= left_edge);
00360   if (pitsync_linear_version >= 4)
00361     return check_pitch_sync3 (projection_left, projection_right, zero_count,
00362       pitch, pitch_error, projection,
00363       projection_scale, occupation_count, seg_list,
00364       start, end);
00365   array_origin = left_edge - pitch;
00366   cutpts = (FPCUTPT *) alloc_mem ((right_edge - left_edge + pitch * 2 + 1)
00367     * sizeof (FPCUTPT));
00368   for (x = array_origin; x < left_edge; x++)
00369                                  //free cuts
00370     cutpts[x - array_origin].setup (cutpts, array_origin, projection, zero_count, pitch, x, 0);
00371   for (offset = 0; offset <= pitch_error; offset++, x++)
00372                                  //not quite free
00373     cutpts[x - array_origin].setup (cutpts, array_origin, projection, zero_count, pitch, x, offset);
00374 
00375   this_it = *blob_it;
00376   best_cost = MAX_FLOAT32;
00377   best_end = NULL;
00378   this_box = box_next (&this_it);//first box
00379   next_box = box_next (&this_it);//second box
00380   blob_index = 1;
00381   while (x < right_edge - pitch_error) {
00382     if (x > this_box.right () + pitch_error && blob_index < blob_count) {
00383       this_box = next_box;
00384       next_box = box_next (&this_it);
00385       blob_index++;
00386     }
00387     faking = FALSE;
00388     mid_cut = FALSE;
00389     if (x <= this_box.left ())
00390       offset = 0;
00391     else if (x <= this_box.left () + pitch_error)
00392       offset = x - this_box.left ();
00393     else if (x >= this_box.right ())
00394       offset = 0;
00395     else if (x >= next_box.left () && blob_index < blob_count) {
00396       offset = x - next_box.left ();
00397       if (this_box.right () - x < offset)
00398         offset = this_box.right () - x;
00399     }
00400     else if (x >= this_box.right () - pitch_error)
00401       offset = this_box.right () - x;
00402     else if (x - this_box.left () > pitch * pitsync_joined_edge
00403     && this_box.right () - x > pitch * pitsync_joined_edge) {
00404       mid_cut = TRUE;
00405       offset = 0;
00406     }
00407     else {
00408       faking = TRUE;
00409       offset = projection->pile_count (x);
00410     }
00411     cutpts[x - array_origin].assign (cutpts, array_origin, x,
00412       faking, mid_cut, offset, projection,
00413       projection_scale, zero_count, pitch,
00414       pitch_error);
00415     x++;
00416   }
00417 
00418   best_fake = MAX_INT16;
00419   best_cost = MAX_INT32;
00420   best_count = MAX_INT16;
00421   while (x < right_edge + pitch) {
00422     offset = x < right_edge ? right_edge - x : 0;
00423     cutpts[x - array_origin].assign (cutpts, array_origin, x,
00424       FALSE, FALSE, offset, projection,
00425       projection_scale, zero_count, pitch,
00426       pitch_error);
00427     cutpts[x - array_origin].terminal = TRUE;
00428     if (cutpts[x - array_origin].index () +
00429     cutpts[x - array_origin].fake_count <= best_count + best_fake) {
00430       if (cutpts[x - array_origin].fake_count < best_fake
00431         || (cutpts[x - array_origin].fake_count == best_fake
00432       && cutpts[x - array_origin].cost_function () < best_cost)) {
00433         best_fake = cutpts[x - array_origin].fake_count;
00434         best_cost = cutpts[x - array_origin].cost_function ();
00435         best_left_x = x;
00436         best_right_x = x;
00437         best_count = cutpts[x - array_origin].index ();
00438       }
00439       else if (cutpts[x - array_origin].fake_count == best_fake
00440         && x == best_right_x + 1
00441       && cutpts[x - array_origin].cost_function () == best_cost) {
00442       //exactly equal
00443         best_right_x = x;
00444       }
00445     }
00446     x++;
00447   }
00448   ASSERT_HOST (best_fake < MAX_INT16);
00449 
00450   best_end = &cutpts[(best_left_x + best_right_x) / 2 - array_origin];
00451   if (this_box.right () == textord_test_x
00452   && this_box.top () == textord_test_y) {
00453     for (x = left_edge - pitch; x < right_edge + pitch; x++) {
00454       tprintf ("x=%d, C=%g, s=%g, sq=%g, prev=%d\n",
00455         x, cutpts[x - array_origin].cost_function (),
00456         cutpts[x - array_origin].sum (),
00457         cutpts[x - array_origin].squares (),
00458         cutpts[x - array_origin].previous ()->position ());
00459     }
00460   }
00461   occupation_count = -1;
00462   do {
00463     for (x = best_end->position () - pitch + pitch_error;
00464       x < best_end->position () - pitch_error
00465       && projection->pile_count (x) == 0; x++);
00466     if (x < best_end->position () - pitch_error)
00467       occupation_count++;
00468                                  //copy it
00469     segpt = new FPSEGPT (best_end);
00470     seg_it.add_before_then_move (segpt);
00471     best_end = best_end->previous ();
00472   }
00473   while (best_end != NULL);
00474   seg_it.move_to_last ();
00475   mean_sum = seg_it.data ()->sum ();
00476   mean_sum = mean_sum * mean_sum / best_count;
00477   if (seg_it.data ()->squares () - mean_sum < 0)
00478     tprintf ("Impossible sqsum=%g, mean=%g, total=%d\n",
00479       seg_it.data ()->squares (), seg_it.data ()->sum (), best_count);
00480   free_mem(cutpts);
00481   //      tprintf("blob_count=%d, pitch=%d, sync=%g, occ=%d\n",
00482   //              blob_count,pitch,seg_it.data()->squares()-mean_sum,
00483   //              occupation_count);
00484   return seg_it.data ()->squares () - mean_sum;
00485 }
00486 
00487 
00488 /**********************************************************************
00489  * check_pitch_sync
00490  *
00491  * Construct the lattice of possible segmentation points and choose the
00492  * optimal path. Return the optimal path only.
00493  * The return value is a measure of goodness of the sync.
00494  **********************************************************************/
00495 
00496 double check_pitch_sync3(                          //find segmentation
00497                          inT16 projection_left,    //edges //to be considered 0
00498                          inT16 projection_right,
00499                          inT16 zero_count,
00500                          inT16 pitch,              //pitch estimate
00501                          inT16 pitch_error,        //tolerance
00502                          STATS *projection,        //vertical
00503                          float projection_scale,   //scale factor
00504                          inT16 &occupation_count,  //no of occupied cells
00505                          FPSEGPT_LIST *seg_list,   //output list
00506                          inT16 start,              //start of good range
00507                          inT16 end                 //end of good range
00508                         ) {
00509   BOOL8 faking;                  //illegal cut pt
00510   BOOL8 mid_cut;                 //cheap cut pt.
00511   inT16 left_edge;               //of word
00512   inT16 right_edge;              //of word
00513   inT16 x;                       //current coord
00514   inT16 array_origin;            //x coord of array
00515   inT16 offset;                  //dist to legal area
00516   inT16 projection_offset;       //from scaled projection
00517   inT16 prev_zero;               //previous zero dist
00518   inT16 next_zero;               //next zero dist
00519   inT16 zero_offset;             //scan window
00520   inT16 best_left_x = 0;         //for equals
00521   inT16 best_right_x = 0;        //right edge
00522   FPSEGPT *segpt;                //segment point
00523   FPCUTPT *cutpts;               //array of points
00524   BOOL8 *mins;                   //local min results
00525   int minindex;                  //next input position
00526   int test_index;                //index to mins
00527   double best_cost;              //best path
00528   double mean_sum;               //computes result
00529   FPCUTPT *best_end;             //end of best path
00530   inT16 best_fake;               //best fake level
00531   inT16 best_count;              //no of cuts
00532   FPSEGPT_IT seg_it = seg_list;  //output iterator
00533 
00534   end = (end - start) % pitch;
00535   if (pitch < 3)
00536     pitch = 3;                   //nothing ludicrous
00537   if ((pitch - 3) / 2 < pitch_error)
00538     pitch_error = (pitch - 3) / 2;
00539                                  //min dist of zero
00540   zero_offset = (inT16) (pitch * pitsync_joined_edge);
00541   for (left_edge = projection_left; projection->pile_count (left_edge) == 0
00542     && left_edge < projection_right; left_edge++);
00543   for (right_edge = projection_right; projection->pile_count (right_edge) == 0
00544     && right_edge > left_edge; right_edge--);
00545   array_origin = left_edge - pitch;
00546   cutpts = (FPCUTPT *) alloc_mem ((right_edge - left_edge + pitch * 2 + 1)
00547     * sizeof (FPCUTPT));
00548   mins = (BOOL8 *) alloc_mem ((pitch_error * 2 + 1) * sizeof (BOOL8));
00549   for (x = array_origin; x < left_edge; x++)
00550                                  //free cuts
00551     cutpts[x - array_origin].setup (cutpts, array_origin, projection, zero_count, pitch, x, 0);
00552   prev_zero = left_edge - 1;
00553   for (offset = 0; offset <= pitch_error; offset++, x++)
00554                                  //not quite free
00555     cutpts[x - array_origin].setup (cutpts, array_origin, projection, zero_count, pitch, x, offset);
00556 
00557   best_cost = MAX_FLOAT32;
00558   best_end = NULL;
00559   for (offset = -pitch_error, minindex = 0; offset < pitch_error;
00560     offset++, minindex++)
00561   mins[minindex] = projection->local_min (x + offset);
00562   next_zero = x + zero_offset + 1;
00563   for (offset = next_zero - 1; offset >= x; offset--) {
00564     if (projection->pile_count (offset) <= zero_count) {
00565       next_zero = offset;
00566       break;
00567     }
00568   }
00569   while (x < right_edge - pitch_error) {
00570     mins[minindex] = projection->local_min (x + pitch_error);
00571     minindex++;
00572     if (minindex > pitch_error * 2)
00573       minindex = 0;
00574     faking = FALSE;
00575     mid_cut = FALSE;
00576     offset = 0;
00577     if (projection->pile_count (x) <= zero_count) {
00578       prev_zero = x;
00579     }
00580     else {
00581       for (offset = 1; offset <= pitch_error; offset++)
00582         if (projection->pile_count (x + offset) <= zero_count
00583         || projection->pile_count (x - offset) <= zero_count)
00584           break;
00585     }
00586     if (offset > pitch_error) {
00587       if (x - prev_zero > zero_offset && next_zero - x > zero_offset) {
00588         for (offset = 0; offset <= pitch_error; offset++) {
00589           test_index = minindex + pitch_error + offset;
00590           if (test_index > pitch_error * 2)
00591             test_index -= pitch_error * 2 + 1;
00592           if (mins[test_index])
00593             break;
00594           test_index = minindex + pitch_error - offset;
00595           if (test_index > pitch_error * 2)
00596             test_index -= pitch_error * 2 + 1;
00597           if (mins[test_index])
00598             break;
00599         }
00600       }
00601       if (offset > pitch_error) {
00602         offset = projection->pile_count (x);
00603         faking = TRUE;
00604       }
00605       else {
00606         projection_offset =
00607           (inT16) (projection->pile_count (x) / projection_scale);
00608         if (projection_offset > offset)
00609           offset = projection_offset;
00610         mid_cut = TRUE;
00611       }
00612     }
00613     if ((start == 0 && end == 0)
00614       || !textord_fast_pitch_test
00615       || (x - projection_left - start) % pitch <= end)
00616       cutpts[x - array_origin].assign (cutpts, array_origin, x,
00617         faking, mid_cut, offset, projection,
00618         projection_scale, zero_count, pitch,
00619         pitch_error);
00620     else
00621       cutpts[x - array_origin].assign_cheap (cutpts, array_origin, x,
00622         faking, mid_cut, offset,
00623         projection, projection_scale,
00624         zero_count, pitch,
00625         pitch_error);
00626     x++;
00627     if (next_zero < x || next_zero == x + zero_offset)
00628       next_zero = x + zero_offset + 1;
00629     if (projection->pile_count (x + zero_offset) <= zero_count)
00630       next_zero = x + zero_offset;
00631   }
00632 
00633   best_fake = MAX_INT16;
00634   best_cost = MAX_INT32;
00635   best_count = MAX_INT16;
00636   while (x < right_edge + pitch) {
00637     offset = x < right_edge ? right_edge - x : 0;
00638     cutpts[x - array_origin].assign (cutpts, array_origin, x,
00639       FALSE, FALSE, offset, projection,
00640       projection_scale, zero_count, pitch,
00641       pitch_error);
00642     cutpts[x - array_origin].terminal = TRUE;
00643     if (cutpts[x - array_origin].index () +
00644     cutpts[x - array_origin].fake_count <= best_count + best_fake) {
00645       if (cutpts[x - array_origin].fake_count < best_fake
00646         || (cutpts[x - array_origin].fake_count == best_fake
00647       && cutpts[x - array_origin].cost_function () < best_cost)) {
00648         best_fake = cutpts[x - array_origin].fake_count;
00649         best_cost = cutpts[x - array_origin].cost_function ();
00650         best_left_x = x;
00651         best_right_x = x;
00652         best_count = cutpts[x - array_origin].index ();
00653       }
00654       else if (cutpts[x - array_origin].fake_count == best_fake
00655         && x == best_right_x + 1
00656       && cutpts[x - array_origin].cost_function () == best_cost) {
00657       //exactly equal
00658         best_right_x = x;
00659       }
00660     }
00661     x++;
00662   }
00663   ASSERT_HOST (best_fake < MAX_INT16);
00664 
00665   best_end = &cutpts[(best_left_x + best_right_x) / 2 - array_origin];
00666   //      for (x=left_edge-pitch;x<right_edge+pitch;x++)
00667   //      {
00668   //              tprintf("x=%d, C=%g, s=%g, sq=%g, prev=%d\n",
00669   //                      x,cutpts[x-array_origin].cost_function(),
00670   //                      cutpts[x-array_origin].sum(),
00671   //                      cutpts[x-array_origin].squares(),
00672   //                      cutpts[x-array_origin].previous()->position());
00673   //      }
00674   occupation_count = -1;
00675   do {
00676     for (x = best_end->position () - pitch + pitch_error;
00677       x < best_end->position () - pitch_error
00678       && projection->pile_count (x) == 0; x++);
00679     if (x < best_end->position () - pitch_error)
00680       occupation_count++;
00681                                  //copy it
00682     segpt = new FPSEGPT (best_end);
00683     seg_it.add_before_then_move (segpt);
00684     best_end = best_end->previous ();
00685   }
00686   while (best_end != NULL);
00687   seg_it.move_to_last ();
00688   mean_sum = seg_it.data ()->sum ();
00689   mean_sum = mean_sum * mean_sum / best_count;
00690   if (seg_it.data ()->squares () - mean_sum < 0)
00691     tprintf ("Impossible sqsum=%g, mean=%g, total=%d\n",
00692       seg_it.data ()->squares (), seg_it.data ()->sum (), best_count);
00693   free_mem(mins);
00694   free_mem(cutpts);
00695   return seg_it.data ()->squares () - mean_sum;
00696 }