tesseract  4.0.0-1-g2a2b
pitsync1.cpp
Go to the documentation of this file.
1 /**********************************************************************
2  * File: pitsync1.cpp (Formerly pitsync.c)
3  * Description: Code to find the optimum fixed pitch segmentation of some blobs.
4  * Author: Ray Smith
5  * Created: Thu Nov 19 11:48:05 GMT 1992
6  *
7  * (C) Copyright 1992, Hewlett-Packard Ltd.
8  ** Licensed under the Apache License, Version 2.0 (the "License");
9  ** you may not use this file except in compliance with the License.
10  ** You may obtain a copy of the License at
11  ** http://www.apache.org/licenses/LICENSE-2.0
12  ** Unless required by applicable law or agreed to in writing, software
13  ** distributed under the License is distributed on an "AS IS" BASIS,
14  ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  ** See the License for the specific language governing permissions and
16  ** limitations under the License.
17  *
18  **********************************************************************/
19 
20 #include <cfloat> // for FLT_MAX
21 #include <cmath>
22 #include "pitsync1.h"
23 
24 ELISTIZE (FPSEGPT) CLISTIZE (FPSEGPT_LIST)
25 
26 INT_VAR(pitsync_linear_version, 6, "Use new fast algorithm");
27 double_VAR(pitsync_joined_edge, 0.75, "Dist inside big blob for chopping");
29  "Fraction of cut for free cuts");
30 INT_VAR(pitsync_fake_depth, 1, "Max advance fake generation");
31 
32 /**********************************************************************
33  * FPSEGPT::FPSEGPT
34  *
35  * Constructor to make a new FPSEGPT.
36  * The existing FPCUTPT is duplicated.
37  **********************************************************************/
38 
39 FPSEGPT::FPSEGPT( //constructor
40  FPCUTPT *cutpt //create from new form
41  ) {
42  pred = nullptr;
43  mean_sum = cutpt->sum ();
44  sq_sum = cutpt->squares ();
45  cost = cutpt->cost_function ();
46  faked = cutpt->faked;
47  terminal = cutpt->terminal;
48  fake_count = cutpt->fake_count;
49  xpos = cutpt->position ();
50  mid_cuts = cutpt->cheap_cuts ();
51 }
52 
53 
54 /**********************************************************************
55  * FPSEGPT::FPSEGPT
56  *
57  * Constructor to make a new FPSEGPT.
58  **********************************************************************/
59 
60 FPSEGPT::FPSEGPT ( //constructor
61 int16_t x //position
62 ):xpos (x) {
63  pred = nullptr;
64  mean_sum = 0;
65  sq_sum = 0;
66  cost = 0;
67  faked = FALSE;
68  terminal = FALSE;
69  fake_count = 0;
70  mid_cuts = 0;
71 }
72 
73 
74 /**********************************************************************
75  * FPSEGPT::FPSEGPT
76  *
77  * Constructor to make a new FPSEGPT.
78  **********************************************************************/
79 
80 FPSEGPT::FPSEGPT ( //constructor
81 int16_t x, //position
82 BOOL8 faking, //faking this one
83 int16_t offset, //dist to gap
84 int16_t region_index, //segment number
85 int16_t pitch, //proposed pitch
86 int16_t pitch_error, //allowed tolerance
87 FPSEGPT_LIST * prev_list //previous segment
88 )
89 : fake_count(0),
90  xpos(x),
91  mean_sum(0.0),
92  sq_sum(0.0)
93 {
94  int16_t best_fake; //on previous
95  FPSEGPT *segpt; //segment point
96  int32_t dist; //from prev segment
97  double sq_dist; //squared distance
98  double mean; //mean pitch
99  double total; //total dists
100  double factor; //cost function
101  FPSEGPT_IT pred_it = prev_list;//for previuos segment
102 
103  cost = FLT_MAX;
104  pred = nullptr;
105  faked = faking;
106  terminal = FALSE;
107  best_fake = INT16_MAX;
108  mid_cuts = 0;
109  for (pred_it.mark_cycle_pt (); !pred_it.cycled_list (); pred_it.forward ()) {
110  segpt = pred_it.data ();
111  if (segpt->fake_count < best_fake)
112  best_fake = segpt->fake_count;
113  dist = x - segpt->xpos;
114  if (dist >= pitch - pitch_error && dist <= pitch + pitch_error
115  && !segpt->terminal) {
116  total = segpt->mean_sum + dist;
117  sq_dist = dist * dist + segpt->sq_sum + offset * offset;
118  //sum of squarees
119  mean = total / region_index;
120  factor = mean - pitch;
121  factor *= factor;
122  factor += sq_dist / (region_index) - mean * mean;
123  if (factor < cost) {
124  cost = factor; //find least cost
125  pred = segpt; //save path
126  mean_sum = total;
127  sq_sum = sq_dist;
128  fake_count = segpt->fake_count + faked;
129  }
130  }
131  }
132  if (fake_count > best_fake + 1)
133  pred = nullptr; //fail it
134 }
135 
136 /**********************************************************************
137  * check_pitch_sync
138  *
139  * Construct the lattice of possible segmentation points and choose the
140  * optimal path. Return the optimal path only.
141  * The return value is a measure of goodness of the sync.
142  **********************************************************************/
143 
144 double check_pitch_sync( //find segmentation
145  BLOBNBOX_IT *blob_it, //blobs to do
146  int16_t blob_count, //no of blobs
147  int16_t pitch, //pitch estimate
148  int16_t pitch_error, //tolerance
149  STATS *projection, //vertical
150  FPSEGPT_LIST *seg_list //output list
151  ) {
152  int16_t x; //current coord
153  int16_t min_index; //blob number
154  int16_t max_index; //blob number
155  int16_t left_edge; //of word
156  int16_t right_edge; //of word
157  int16_t right_max; //max allowed x
158  int16_t min_x; //in this region
159  int16_t max_x;
160  int16_t region_index;
161  int16_t best_region_index = 0; //for best result
162  int16_t offset; //dist to legal area
163  int16_t left_best_x; //edge of good region
164  int16_t right_best_x; //right edge
165  TBOX min_box; //bounding box
166  TBOX max_box; //bounding box
167  TBOX next_box; //box of next blob
168  FPSEGPT *segpt; //segment point
169  FPSEGPT_LIST *segpts; //points in a segment
170  double best_cost; //best path
171  double mean_sum; //computes result
172  FPSEGPT *best_end; //end of best path
173  BLOBNBOX_IT min_it; //copy iterator
174  BLOBNBOX_IT max_it; //copy iterator
175  FPSEGPT_IT segpt_it; //iterator
176  //output segments
177  FPSEGPT_IT outseg_it = seg_list;
178  FPSEGPT_LIST_CLIST lattice; //list of lists
179  //region iterator
180  FPSEGPT_LIST_C_IT lattice_it = &lattice;
181 
182  // tprintf("Computing sync on word of %d blobs with pitch %d\n",
183  // blob_count, pitch);
184  // if (blob_count==8 && pitch==27)
185  // projection->print(stdout,TRUE);
186  if (pitch < 3)
187  pitch = 3; //nothing ludicrous
188  if ((pitch - 3) / 2 < pitch_error)
189  pitch_error = (pitch - 3) / 2;
190  min_it = *blob_it;
191  min_box = box_next (&min_it); //get box
192  // if (blob_count==8 && pitch==27)
193  // tprintf("1st box at (%d,%d)->(%d,%d)\n",
194  // min_box.left(),min_box.bottom(),
195  // min_box.right(),min_box.top());
196  //left of word
197  left_edge = min_box.left () + pitch_error;
198  for (min_index = 1; min_index < blob_count; min_index++) {
199  min_box = box_next (&min_it);
200  // if (blob_count==8 && pitch==27)
201  // tprintf("Box at (%d,%d)->(%d,%d)\n",
202  // min_box.left(),min_box.bottom(),
203  // min_box.right(),min_box.top());
204  }
205  right_edge = min_box.right (); //end of word
206  max_x = left_edge;
207  //min permissible
208  min_x = max_x - pitch + pitch_error * 2 + 1;
209  right_max = right_edge + pitch - pitch_error - 1;
210  segpts = new FPSEGPT_LIST; //list of points
211  segpt_it.set_to_list (segpts);
212  for (x = min_x; x <= max_x; x++) {
213  segpt = new FPSEGPT (x); //make a new one
214  //put in list
215  segpt_it.add_after_then_move (segpt);
216  }
217  //first segment
218  lattice_it.add_before_then_move (segpts);
219  min_index = 0;
220  region_index = 1;
221  best_cost = FLT_MAX;
222  best_end = nullptr;
223  min_it = *blob_it;
224  min_box = box_next (&min_it); //first box
225  do {
226  left_best_x = -1;
227  right_best_x = -1;
228  segpts = new FPSEGPT_LIST; //list of points
229  segpt_it.set_to_list (segpts);
230  min_x += pitch - pitch_error;//next limits
231  max_x += pitch + pitch_error;
232  while (min_box.right () < min_x && min_index < blob_count) {
233  min_index++;
234  min_box = box_next (&min_it);
235  }
236  max_it = min_it;
237  max_index = min_index;
238  max_box = min_box;
239  next_box = box_next (&max_it);
240  for (x = min_x; x <= max_x && x <= right_max; x++) {
241  while (x < right_edge && max_index < blob_count
242  && x > max_box.right ()) {
243  max_index++;
244  max_box = next_box;
245  next_box = box_next (&max_it);
246  }
247  if (x <= max_box.left () + pitch_error
248  || x >= max_box.right () - pitch_error || x >= right_edge
249  || (max_index < blob_count - 1 && x >= next_box.left ())
250  || (x - max_box.left () > pitch * pitsync_joined_edge
251  && max_box.right () - x > pitch * pitsync_joined_edge)) {
252  // || projection->local_min(x))
253  if (x - max_box.left () > 0
254  && x - max_box.left () <= pitch_error)
255  //dist to real break
256  offset = x - max_box.left ();
257  else if (max_box.right () - x > 0
258  && max_box.right () - x <= pitch_error
259  && (max_index >= blob_count - 1
260  || x < next_box.left ()))
261  offset = max_box.right () - x;
262  else
263  offset = 0;
264  // offset=pitsync_offset_freecut_fraction*projection->pile_count(x);
265  segpt = new FPSEGPT (x, FALSE, offset, region_index,
266  pitch, pitch_error, lattice_it.data ());
267  }
268  else {
269  offset = projection->pile_count (x);
270  segpt = new FPSEGPT (x, TRUE, offset, region_index,
271  pitch, pitch_error, lattice_it.data ());
272  }
273  if (segpt->previous () != nullptr) {
274  segpt_it.add_after_then_move (segpt);
275  if (x >= right_edge - pitch_error) {
276  segpt->terminal = TRUE;//no more wanted
277  if (segpt->cost_function () < best_cost) {
278  best_cost = segpt->cost_function ();
279  //find least
280  best_end = segpt;
281  best_region_index = region_index;
282  left_best_x = x;
283  right_best_x = x;
284  }
285  else if (segpt->cost_function () == best_cost
286  && right_best_x == x - 1)
287  right_best_x = x;
288  }
289  }
290  else {
291  delete segpt; //no good
292  }
293  }
294  if (segpts->empty ()) {
295  if (best_end != nullptr)
296  break; //already found one
297  make_illegal_segment (lattice_it.data (), min_box, min_it,
298  region_index, pitch, pitch_error, segpts);
299  }
300  else {
301  if (right_best_x > left_best_x + 1) {
302  left_best_x = (left_best_x + right_best_x + 1) / 2;
303  for (segpt_it.mark_cycle_pt (); !segpt_it.cycled_list ()
304  && segpt_it.data ()->position () != left_best_x;
305  segpt_it.forward ());
306  if (segpt_it.data ()->position () == left_best_x)
307  //middle of region
308  best_end = segpt_it.data ();
309  }
310  }
311  //new segment
312  lattice_it.add_before_then_move (segpts);
313  region_index++;
314  }
315  while (min_x < right_edge);
316  ASSERT_HOST (best_end != nullptr);//must always find some
317 
318  for (lattice_it.mark_cycle_pt (); !lattice_it.cycled_list ();
319  lattice_it.forward ()) {
320  segpts = lattice_it.data ();
321  segpt_it.set_to_list (segpts);
322  // if (blob_count==8 && pitch==27)
323  // {
324  // for (segpt_it.mark_cycle_pt();!segpt_it.cycled_list();segpt_it.forward())
325  // {
326  // segpt=segpt_it.data();
327  // tprintf("At %d, (%x) cost=%g, m=%g, sq=%g, pred=%x\n",
328  // segpt->position(),segpt,segpt->cost_function(),
329  // segpt->sum(),segpt->squares(),segpt->previous());
330  // }
331  // tprintf("\n");
332  // }
333  for (segpt_it.mark_cycle_pt (); !segpt_it.cycled_list ()
334  && segpt_it.data () != best_end; segpt_it.forward ());
335  if (segpt_it.data () == best_end) {
336  //save good one
337  segpt = segpt_it.extract ();
338  outseg_it.add_before_then_move (segpt);
339  best_end = segpt->previous ();
340  }
341  }
342  ASSERT_HOST (best_end == nullptr);
343  ASSERT_HOST (!outseg_it.empty ());
344  outseg_it.move_to_last ();
345  mean_sum = outseg_it.data ()->sum ();
346  mean_sum = mean_sum * mean_sum / best_region_index;
347  if (outseg_it.data ()->squares () - mean_sum < 0)
348  tprintf ("Impossible sqsum=%g, mean=%g, total=%d\n",
349  outseg_it.data ()->squares (), outseg_it.data ()->sum (),
350  best_region_index);
351  lattice.deep_clear (); //shift the lot
352  return outseg_it.data ()->squares () - mean_sum;
353 }
354 
355 
356 /**********************************************************************
357  * make_illegal_segment
358  *
359  * Make a fake set of chop points due to having no legal places.
360  **********************************************************************/
361 
362 void make_illegal_segment( //find segmentation
363  FPSEGPT_LIST *prev_list, //previous segments
364  TBOX blob_box, //bounding box
365  BLOBNBOX_IT blob_it, //iterator
366  int16_t region_index, //number of segment
367  int16_t pitch, //pitch estimate
368  int16_t pitch_error, //tolerance
369  FPSEGPT_LIST *seg_list //output list
370  ) {
371  int16_t x; //current coord
372  int16_t min_x = 0; //in this region
373  int16_t max_x = 0;
374  int16_t offset; //dist to edge
375  FPSEGPT *segpt; //segment point
376  FPSEGPT *prevpt; //previous point
377  float best_cost; //best path
378  FPSEGPT_IT segpt_it = seg_list;//iterator
379  //previous points
380  FPSEGPT_IT prevpt_it = prev_list;
381 
382  best_cost = FLT_MAX;
383  for (prevpt_it.mark_cycle_pt (); !prevpt_it.cycled_list ();
384  prevpt_it.forward ()) {
385  prevpt = prevpt_it.data ();
386  if (prevpt->cost_function () < best_cost) {
387  //find least
388  best_cost = prevpt->cost_function ();
389  min_x = prevpt->position ();
390  max_x = min_x; //limits on coords
391  }
392  else if (prevpt->cost_function () == best_cost) {
393  max_x = prevpt->position ();
394  }
395  }
396  min_x += pitch - pitch_error;
397  max_x += pitch + pitch_error;
398  for (x = min_x; x <= max_x; x++) {
399  while (x > blob_box.right ()) {
400  blob_box = box_next (&blob_it);
401  }
402  offset = x - blob_box.left ();
403  if (blob_box.right () - x < offset)
404  offset = blob_box.right () - x;
405  segpt = new FPSEGPT (x, FALSE, offset,
406  region_index, pitch, pitch_error, prev_list);
407  if (segpt->previous () != nullptr) {
408  ASSERT_HOST (offset >= 0);
409  fprintf (stderr, "made fake at %d\n", x);
410  //make one up
411  segpt_it.add_after_then_move (segpt);
412  segpt->faked = TRUE;
413  segpt->fake_count++;
414  }
415  else
416  delete segpt;
417  }
418 }
int32_t pile_count(int32_t value) const
Definition: statistc.h:78
#define TRUE
Definition: capi.h:51
int16_t cheap_cuts() const
Definition: pithsync.h:83
int32_t position()
Definition: pithsync.h:68
int pitsync_fake_depth
Definition: pitsync1.cpp:30
double pitsync_offset_freecut_fraction
Definition: pitsync1.cpp:29
double squares()
Definition: pithsync.h:74
#define double_VAR(name, val, comment)
Definition: params.h:285
double pitsync_joined_edge
Definition: pitsync1.cpp:27
bool terminal
Definition: pithsync.h:91
Definition: rect.h:34
int16_t fake_count
Definition: pitsync1.h:70
double check_pitch_sync(BLOBNBOX_IT *blob_it, int16_t blob_count, int16_t pitch, int16_t pitch_error, STATS *projection, FPSEGPT_LIST *seg_list)
Definition: pitsync1.cpp:144
double cost_function()
Definition: pithsync.h:71
Definition: statistc.h:33
TBOX box_next(BLOBNBOX_IT *it)
Definition: blobbox.cpp:637
double sum()
Definition: pithsync.h:77
double cost_function()
Definition: pitsync1.h:51
int16_t fake_count
Definition: pithsync.h:92
int16_t left() const
Definition: rect.h:72
#define FALSE
Definition: capi.h:52
ELISTIZE(FPSEGPT) CLISTIZE(FPSEGPT_LIST) int pitsync_linear_version
FPSEGPT * previous()
Definition: pitsync1.h:60
unsigned char BOOL8
Definition: host.h:34
DLLSYM void tprintf(const char *format,...)
Definition: tprintf.cpp:37
FPSEGPT()=default
void make_illegal_segment(FPSEGPT_LIST *prev_list, TBOX blob_box, BLOBNBOX_IT blob_it, int16_t region_index, int16_t pitch, int16_t pitch_error, FPSEGPT_LIST *seg_list)
Definition: pitsync1.cpp:362
bool faked
Definition: pithsync.h:90
int32_t position()
Definition: pitsync1.h:48
int16_t right() const
Definition: rect.h:79
BOOL8 faked
Definition: pitsync1.h:68
CLISTIZE(BLOCK_RES) ELISTIZE(ROW_RES) ELISTIZE(WERD_RES) static const double kStopperAmbiguityThresholdGain
#define INT_VAR(name, val, comment)
Definition: params.h:276
BOOL8 terminal
Definition: pitsync1.h:69
#define ASSERT_HOST(x)
Definition: errcode.h:84