tesseract
3.03
|
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 }