68 "Max number of blobs a big blob can overlap");
71 "Fraction of line spacing for quad");
73 "Fraction of line spacing for outlier");
80 "Factor to expand rows by in expand_rows");
85 "New row made if blob makes row this big");
89 "Min blob height/top to include blob top into xheight stats");
91 "Min pile height to make xheight");
93 "Min pile height to make ascheight");
95 "Min pile height to make descheight");
105 #define MAX_HEIGHT_MODES 12 111 static float MakeRowFromBlobs(
float line_size,
112 BLOBNBOX_IT* blob_it, TO_ROW_IT* row_it) {
114 blob_it->move_to_first();
116 float total_size = 0.0f;
119 for (; !blob_it->empty(); blob_it->forward()) {
120 BLOBNBOX* blob = blob_it->extract();
124 row =
new TO_ROW(blob, top, bottom, line_size);
125 row_it->add_before_then_move(row);
127 row->
add_blob(blob, top, bottom, line_size);
129 total_size += top - bottom;
132 return blob_count > 0 ? total_size / blob_count : total_size;
140 C_OUTLINE_IT ol_it(blob->
out_list());
142 ol_it.set_to_list(ol_it.data()->child());
145 for (ol_it.mark_cycle_pt(); !ol_it.cycled_list(); ol_it.forward()) {
151 bb_it.add_after_then_move(bbox);
154 return MakeRowFromBlobs(block->
line_size, &bb_it, row_it);
165 TO_BLOCK* block, TO_BLOCK_LIST* blocks) {
166 BLOBNBOX_IT blob_it = &block->
blobs;
167 TO_ROW_IT row_it = block->
get_rows();
173 if (block->
blobs.singleton() && allow_sub_blobs) {
174 blob_it.move_to_first();
178 }
else if (block->
blobs.empty()) {
183 blob_it.add_after_then_move(bblob);
185 MakeRowFromBlobs(block->
line_size, &blob_it, &row_it);
187 for (row_it.mark_cycle_pt(); !row_it.cycled_list(); row_it.forward())
204 TO_BLOCK_IT block_it;
206 block_it.set_to_list(port_blocks);
207 for (block_it.mark_cycle_pt(); !block_it.cycled_list();
213 block_it.set_to_list(port_blocks);
214 for (block_it.mark_cycle_pt(); !block_it.cycled_list(); block_it.forward()) {
216 block_it.data()->block->bounding_box().left(),
233 TO_ROW_IT row_it = block->
get_rows ();
235 #ifndef GRAPHICS_DISABLED 245 row_it.move_to_first ();
246 for (row_it.mark_cycle_pt (); !row_it.cycled_list (); row_it.forward ())
248 #ifndef GRAPHICS_DISABLED 251 for (row_it.mark_cycle_pt (); !row_it.cycled_list (); row_it.forward ()) {
272 for (blob_it.mark_cycle_pt(); !blob_it.cycled_list(); blob_it.forward()) {
273 const TBOX& box = blob_it.data()->bounding_box();
276 double error = lms.
Fit(&m, &c);
288 TO_BLOCK_LIST *blocks,
299 TO_BLOCK_IT block_it = blocks;
304 for (block_it.mark_cycle_pt (); !block_it.cycled_list ();
305 block_it.forward ()) {
306 POLY_BLOCK* pb = block_it.data()->block->poly_block();
307 if (pb != NULL && !pb->
IsText())
309 row_count += block_it.data ()->get_rows ()->length ();
311 row_it.set_to_list (block_it.data ()->get_rows ());
312 for (row_it.mark_cycle_pt (); !row_it.cycled_list (); row_it.forward ())
313 blob_count += row_it.data ()->blob_list ()->length ();
315 if (row_count == 0) {
320 gradients = (
float *)
alloc_mem (blob_count *
sizeof (
float));
322 errors = (
float *)
alloc_mem (blob_count *
sizeof (
float));
323 if (gradients == NULL || errors == NULL)
327 for (block_it.mark_cycle_pt (); !block_it.cycled_list ();
328 block_it.forward ()) {
329 POLY_BLOCK* pb = block_it.data()->block->poly_block();
330 if (pb != NULL && !pb->
IsText())
332 row_it.set_to_list (block_it.data ()->get_rows ());
333 for (row_it.mark_cycle_pt (); !row_it.cycled_list (); row_it.forward ()) {
334 row = row_it.data ();
335 blob_count = row->
blob_list ()->length ();
340 blob_count /= row_err;
341 for (blob_count /= row_err; blob_count > 0; blob_count--) {
342 gradients[row_index] = row->
line_m ();
349 gradients[row_index] = row->
line_m ();
355 if (row_index == 0) {
357 for (block_it.mark_cycle_pt (); !block_it.cycled_list ();
358 block_it.forward ()) {
359 POLY_BLOCK* pb = block_it.data()->block->poly_block();
360 if (pb != NULL && !pb->
IsText())
362 row_it.set_to_list (block_it.data ()->get_rows ());
363 for (row_it.mark_cycle_pt (); !row_it.cycled_list ();
365 row = row_it.data ();
366 gradients[row_index] = row->
line_m ();
372 row_count = row_index;
374 gradients, row_count);
375 page_m = gradients[row_index];
378 page_err = errors[row_index];
398 (overlap * 2 < ibox.
width() && overlap < dotbox.
width()))
408 const double kHeightFraction = 0.6;
409 double target_height =
MIN(dotbox.
bottom(), ibox.
top());
411 target_height *= kHeightFraction;
412 int left_min = dotbox.
left() - dotbox.
width();
413 int middle = (dotbox.
left() + dotbox.
right())/2;
414 int right_max = dotbox.
right() + dotbox.
width();
419 bool found_left =
false;
420 bool found_right =
false;
421 bool in_left =
false;
422 bool in_right =
false;
424 C_OUTLINE_IT o_it = blob->
out_list();
425 for (o_it.mark_cycle_pt(); !o_it.cycled_list(); o_it.forward()) {
429 for (
int step = 0; step < length; pos += outline->
step(step++)) {
432 if (x >= left_min && x < middle && !found_left) {
435 if (y > left_maxy) left_maxy = y;
436 if (y < left_miny) left_miny = y;
438 left_maxy = left_miny = y;
441 }
else if (in_left) {
443 if (left_maxy - left_miny > target_height) {
450 if (x <= right_max && x > middle && !found_right) {
453 if (y > right_maxy) right_maxy = y;
454 if (y < right_miny) right_miny = y;
456 right_maxy = right_miny = y;
459 }
else if (in_right) {
461 if (right_maxy - right_miny > target_height) {
474 TO_ROW_IT row_it = block->
get_rows ();
475 for (row_it.mark_cycle_pt (); !row_it.cycled_list (); row_it.forward ()) {
476 TO_ROW* row = row_it.data();
480 for (b_it.mark_cycle_pt(); !b_it.cycled_list(); b_it.forward()) {
485 STATS hstats(0, max_height + 1);
486 for (b_it.mark_cycle_pt(); !b_it.cycled_list(); b_it.forward()) {
492 float xheight = hstats.
median();
495 for (b_it.mark_cycle_pt(); !b_it.cycled_list(); b_it.forward()) {
501 if (dot_of_i(blob, prev, row))
504 if (!b_it.at_last()) {
505 BLOBNBOX* next = b_it.data_relative(1);
506 if (dot_of_i(blob, next, row))
510 delete blob->
cblob();
511 delete b_it.extract();
533 BLOBNBOX_IT blob_it = &block->
blobs;
534 TO_ROW_IT row_it = block->
get_rows ();
536 #ifndef GRAPHICS_DISABLED 553 expand_rows(page_tr, block, gradient, rotation, block_edge, testing_on);
554 blob_it.set_to_list (&block->
blobs);
555 row_it.set_to_list (block->
get_rows ());
556 for (row_it.mark_cycle_pt (); !row_it.cycled_list (); row_it.forward ())
557 blob_it.add_list_after (row_it.data ()->blob_list ());
561 blob_it.set_to_list (&block->
blobs);
565 blob_it.set_to_list (&block->
blobs);
595 TO_ROW_IT row_it = block->
get_rows ();
596 BLOBNBOX_IT blob_it = &block->
blobs;
598 if (row_it.length () == 0)
603 min_y = block_box.
bottom () - 1;
604 max_y = block_box.
top () + 1;
605 for (row_it.mark_cycle_pt (); !row_it.cycled_list (); row_it.forward ()) {
606 line_index = (
inT32) floor (row_it.data ()->intercept ());
607 if (line_index <= min_y)
608 min_y = line_index - 1;
609 if (line_index >= max_y)
610 max_y = line_index + 1;
612 line_count = max_y - min_y + 1;
617 if (deltas == NULL || occupation == NULL)
628 max_y - min_y + 1, occupation, deltas);
629 #ifndef GRAPHICS_DISABLED 635 for (row_it.mark_cycle_pt (); !row_it.cycled_list (); row_it.forward ()) {
636 row = row_it.data ();
638 distance = deltas[line_index - min_y];
640 line_index, &row_it, testing_on)) {
641 #ifndef GRAPHICS_DISABLED 646 blob_it.add_list_after (row_it.data ()->blob_list ());
647 delete row_it.extract ();
650 for (row_it.mark_cycle_pt (); !row_it.cycled_list (); row_it.forward ()) {
651 blob_it.add_list_after (row_it.data ()->blob_list ());
680 tprintf (
"Row at %g(%g), dropout dist=%d,",
684 abs_dist = -distance;
690 if (abs_dist > dist_limit) {
692 tprintf (
" too far - deleting\n");
696 if ((distance < 0 && !row_it->at_last ())
697 || (distance >= 0 && !row_it->at_first ())) {
698 row_offset = row_inc;
700 next_row = row_it->data_relative (row_offset);
703 && next_index < line_index
704 && next_index > line_index + distance + distance)
706 && next_index > line_index
707 && next_index < line_index + distance + distance)) {
709 tprintf (
" nearer neighbour (%d) at %g\n",
710 line_index + distance - next_index,
715 else if (next_index == line_index
716 || next_index == line_index + distance + distance) {
719 tprintf (
" equal but more believable at %g (%g/%g)\n",
727 row_offset += row_inc;
729 while ((next_index == line_index
730 || next_index == line_index + distance + distance)
731 && row_offset < row_it->length ());
753 TO_ROW_IT row_it = block->
get_rows ();
758 length = sqrt (gradient * gradient + 1);
759 rotation =
FCOORD (1 / length, -gradient / length);
760 for (row_it.mark_cycle_pt (); !row_it.cycled_list (); row_it.forward ()) {
761 row = row_it.data ();
763 for (blob_it.mark_cycle_pt (); !blob_it.cycled_list ();
764 blob_it.forward ()) {
765 blob = blob_it.data ();
767 blob_box.
rotate (rotation);
793 TO_ROW_IT row_it = block->
get_rows ();
800 line_count = max_y - min_y + 1;
801 length = sqrt (gradient * gradient + 1);
802 rotation =
FCOORD (1 / length, -gradient / length);
803 for (line_index = 0; line_index < line_count; line_index++)
804 deltas[line_index] = 0;
805 for (row_it.mark_cycle_pt (); !row_it.cycled_list (); row_it.forward ()) {
806 row = row_it.data ();
808 for (blob_it.mark_cycle_pt (); !blob_it.cycled_list ();
809 blob_it.forward ()) {
810 blob = blob_it.data ();
812 blob_box.
rotate (rotation);
813 int32_t width = blob_box.
right() - blob_box.
left();
814 index = blob_box.
bottom() - min_y;
817 deltas[index] += width;
818 index = blob_box.
top() - min_y;
820 deltas[index] -= width;
823 occupation[0] = deltas[0];
824 for (line_index = 1; line_index < line_count; line_index++)
825 occupation[line_index] = occupation[line_index - 1] + deltas[line_index];
852 if (low_window + high_window < line_count) {
853 for (sum = 0, high_index = 0; high_index < low_window; high_index++)
854 sum += occupation[high_index];
855 for (low_index = 0; low_index < high_window; low_index++, high_index++)
856 sum += occupation[high_index];
857 min_occ = occupation[0];
859 for (test_index = 1; test_index < high_index; test_index++) {
860 if (occupation[test_index] <= min_occ) {
861 min_occ = occupation[test_index];
862 min_index = test_index;
865 for (line_index = 0; line_index < low_window; line_index++)
866 thresholds[line_index] = (sum - min_occ) / divisor + min_occ;
868 for (low_index = 0; high_index < line_count; low_index++, high_index++) {
869 sum -= occupation[low_index];
870 sum += occupation[high_index];
871 if (occupation[high_index] <= min_occ) {
873 min_occ = occupation[high_index];
874 min_index = high_index;
877 if (min_index <= low_index) {
878 min_occ = occupation[low_index + 1];
879 min_index = low_index + 1;
880 for (test_index = low_index + 2; test_index <= high_index;
882 if (occupation[test_index] <= min_occ) {
883 min_occ = occupation[test_index];
885 min_index = test_index;
889 thresholds[line_index++] = (sum - min_occ) / divisor + min_occ;
893 min_occ = occupation[0];
895 for (sum = 0, low_index = 0; low_index < line_count; low_index++) {
896 if (occupation[low_index] < min_occ) {
897 min_occ = occupation[low_index];
898 min_index = low_index;
900 sum += occupation[low_index];
904 for (; line_index < line_count; line_index++)
905 thresholds[line_index] = (sum - min_occ) / divisor + min_occ;
924 inT32 prev_threshold;
926 distance = -line_count;
931 prev_threshold = thresholds[line_index];
933 thresholds[line_index] = distance;
936 while (line_index < line_count
937 && (occupation[line_index] < thresholds[line_index]
938 || occupation[line_index - 1] >= prev_threshold));
939 if (line_index < line_count) {
940 back_index = line_index - 1;
942 while (next_dist < -distance && back_index >= 0) {
943 thresholds[back_index] = next_dist;
951 while (line_index < line_count);
972 float y_bottom, y_top;
976 BLOBNBOX_IT blob_it = &block->
blobs;
977 TO_ROW_IT row_it = block->
get_rows ();
979 #ifndef GRAPHICS_DISABLED 988 if (block->
get_rows ()->length () == 0)
994 if (block->
get_rows ()->length () == 0)
1003 row_it.move_to_last ();
1005 row = row_it.data ();
1006 y_max = row->
max_y ();
1007 y_min = row->
min_y ();
1013 if (y_min > y_bottom) {
1015 tprintf(
"Expanding bottom of row at %f from %f to %f\n",
1018 swallowed_row =
TRUE;
1019 while (swallowed_row && !row_it.at_last ()) {
1020 swallowed_row =
FALSE;
1022 test_row = row_it.data_relative (1);
1024 if (test_row->
max_y () > y_bottom) {
1025 if (test_row->
min_y () > y_bottom) {
1029 #ifndef GRAPHICS_DISABLED 1037 blob_it.set_to_list (row->
blob_list ());
1038 blob_it.add_list_after (test_row->
blob_list ());
1040 delete row_it.extract ();
1042 swallowed_row =
TRUE;
1044 else if (test_row->
max_y () < y_min) {
1046 y_bottom = test_row->
max_y ();
1048 tprintf(
"Truncating limit to %f due to touching row at %f\n",
1054 tprintf(
"Not expanding limit beyond %f due to touching row at %f\n",
1061 if (y_max < y_top) {
1063 tprintf(
"Expanding top of row at %f from %f to %f\n",
1065 swallowed_row =
TRUE;
1066 while (swallowed_row && !row_it.at_first ()) {
1067 swallowed_row =
FALSE;
1069 test_row = row_it.data_relative (-1);
1070 if (test_row->
min_y () < y_top) {
1071 if (test_row->
max_y () < y_top) {
1075 blob_it.set_to_list (row->
blob_list ());
1076 #ifndef GRAPHICS_DISABLED 1084 blob_it.add_list_after (test_row->
blob_list ());
1086 delete row_it.extract ();
1088 swallowed_row =
TRUE;
1090 else if (test_row->
min_y () < y_max) {
1092 y_top = test_row->
min_y ();
1094 tprintf(
"Truncating limit to %f due to touching row at %f\n",
1100 tprintf(
"Not expanding limit beyond %f due to touching row at %f\n",
1111 while (!row_it.at_last ());
1127 TO_ROW_IT row_it = block->
get_rows ();
1130 tprintf(
"Adjusting row limits for block(%d,%d)\n",
1133 for (row_it.mark_cycle_pt (); !row_it.cycled_list (); row_it.forward ()) {
1134 row = row_it.data ();
1137 tprintf(
"Row at %f has min %f, max %f, size %f\n",
1164 TO_ROW_IT row_it = block->
get_rows ();
1166 inT16 rowcount = row_it.length ();
1174 row_it.move_to_last ();
1176 row = row_it.data ();
1177 if (prev_row != NULL) {
1178 rows[rowcount++] = prev_row;
1181 tprintf (
"Row at %g yields spacing of %g\n",
1187 while (!row_it.at_last ());
1192 tprintf (
"Blob based spacing=(%g,%g), offset=%g",
1197 iqr = rows[row_index]->
spacing;
1200 iqr -= rows[row_index]->
spacing;
1203 block->
key_row = rows[row_index];
1205 tprintf (
" row based=%g(%g)", rows[row_index]->spacing, iqr);
1209 if (rows[row_index]->spacing < block->line_spacing
1210 && rows[row_index]->spacing > block->
line_size)
1214 else if (rows[row_index]->spacing > block->
line_spacing)
1219 if (rows[row_index]->spacing < block->line_spacing)
1235 tprintf (
"\nEstimate line size=%g, spacing=%g, offset=%g\n",
1277 inT32 min_height, max_height;
1278 TO_ROW_IT row_it = block->
get_rows();
1279 if (row_it.empty())
return;
1284 STATS row_asc_xheights(min_height, max_height + 1);
1285 STATS row_asc_ascrise(static_cast<int>(min_height * asc_frac_xheight),
1286 static_cast<int>(max_height * asc_frac_xheight) + 1);
1287 int min_desc_height =
static_cast<int>(min_height * desc_frac_xheight);
1288 int max_desc_height =
static_cast<int>(max_height * desc_frac_xheight);
1289 STATS row_asc_descdrop(min_desc_height, max_desc_height + 1);
1290 STATS row_desc_xheights(min_height, max_height + 1);
1291 STATS row_desc_descdrop(min_desc_height, max_desc_height + 1);
1292 STATS row_cap_xheights(min_height, max_height + 1);
1293 STATS row_cap_floating_xheights(min_height, max_height + 1);
1294 for (row_it.mark_cycle_pt(); !row_it.cycled_list(); row_it.forward()) {
1295 row = row_it.data();
1303 row_asc_xheights.
add(static_cast<inT32>(row->
xheight),
1305 row_asc_ascrise.
add(static_cast<inT32>(row->
ascrise),
1307 row_asc_descdrop.
add(static_cast<inT32>(-row->
descdrop),
1310 row_desc_xheights.
add(static_cast<inT32>(row->
xheight),
1312 row_desc_descdrop.
add(static_cast<inT32>(-row->
descdrop),
1316 &row_cap_xheights, &row_cap_floating_xheights);
1320 float xheight = 0.0;
1321 float ascrise = 0.0;
1322 float descdrop = 0.0;
1326 xheight = row_asc_xheights.
median();
1327 ascrise = row_asc_ascrise.
median();
1328 descdrop = -row_asc_descdrop.
median();
1329 }
else if (row_desc_xheights.
get_total() > 0) {
1331 xheight = row_desc_xheights.
median();
1332 descdrop = -row_desc_descdrop.
median();
1333 }
else if (row_cap_xheights.
get_total() > 0) {
1344 min_height, max_height, &(xheight), &(ascrise));
1352 bool corrected_xheight =
false;
1355 corrected_xheight =
true;
1357 if (corrected_xheight || ascrise <= 0.0) {
1358 ascrise = xheight * asc_frac_xheight;
1360 if (corrected_xheight || descdrop >= 0.0) {
1361 descdrop = -(xheight * desc_frac_xheight);
1366 tprintf(
"Block average xheight=%.4f, ascrise=%.4f, descdrop=%.4f\n",
1367 xheight, ascrise, descdrop);
1370 for (row_it.mark_cycle_pt(); !row_it.cycled_list(); row_it.forward()) {
1386 int block_line_size) {
1394 int min_height, max_height;
1396 STATS heights(min_height, max_height + 1);
1397 STATS floating_heights(min_height, max_height + 1);
1399 &heights, &floating_heights);
1405 rotation.
y() == 0.0,
1406 min_height, max_height,
1410 row->
descdrop =
static_cast<float>(
1424 int max_height,
STATS *heights,
STATS *floating_heights) {
1431 if (blob_it.empty())
return;
1432 bool has_rep_chars =
1435 blob = blob_it.data();
1444 top -= gradient * xcentre + row->
parallel_c();
1445 if (top >= min_height && top <= max_height) {
1446 heights->
add(static_cast<inT32>(floor(top + 0.5)), 1);
1448 floating_heights->
add(static_cast<inT32>(floor(top + 0.5)), 1);
1456 while (!blob_it.at_first() &&
1457 blob_it.data()->repeated_set() == repeated_set) {
1460 tprintf(
"Skipping repeated char when computing xheight\n");
1465 }
while (!blob_it.at_first());
1485 STATS *heights,
STATS *floating_heights,
bool cap_only,
int min_height,
1486 int max_height,
float *xheight,
float *ascrise) {
1487 int blob_index = heights->
mode();
1488 int blob_count = heights->
pile_count(blob_index);
1490 tprintf(
"min_height=%d, max_height=%d, mode=%d, count=%d, total=%d\n",
1491 min_height, max_height, blob_index, blob_count,
1494 floating_heights->
print();
1496 if (blob_count == 0)
return 0;
1498 bool in_best_pile =
FALSE;
1503 if (cap_only && mode_count > 1)
1507 tprintf(
"found %d modes: ", mode_count);
1508 for (x = 0; x < mode_count; x++)
tprintf(
"%d ", modes[x]);
1512 for (x = 0; x < mode_count - 1; x++) {
1513 if (modes[x] != prev_size + 1)
1514 in_best_pile =
FALSE;
1515 int modes_x_count = heights->
pile_count(modes[x]) -
1518 (in_best_pile || modes_x_count > best_count)) {
1519 for (
int asc = x + 1; asc < mode_count; asc++) {
1521 static_cast<float>(modes[asc]) / static_cast<float>(modes[x]);
1526 if (modes_x_count > best_count) {
1527 in_best_pile =
true;
1528 best_count = modes_x_count;
1531 tprintf(
"X=%d, asc=%d, count=%d, ratio=%g\n",
1532 modes[x], modes[asc]-modes[x], modes_x_count, ratio);
1534 prev_size = modes[x];
1535 *xheight =
static_cast<float>(modes[x]);
1536 *ascrise =
static_cast<float>(modes[asc] - modes[x]);
1541 if (*xheight == 0) {
1548 if (floating_heights->
get_total() > 0) {
1549 for (x = min_height; x < max_height; ++x) {
1552 blob_index = heights->
mode();
1553 for (x = min_height; x < max_height; ++x) {
1557 *xheight =
static_cast<float>(blob_index);
1559 best_count = heights->
pile_count(blob_index);
1561 tprintf(
"Single mode xheight set to %g\n", *xheight);
1563 tprintf(
"Multi-mode xheight set to %g, asc=%g\n", *xheight, *ascrise);
1581 int xheight_blob_count,
STATS *asc_heights) {
1585 i_min =
static_cast<int>(
1592 int num_potential_asc = 0;
1593 for (
int i = i_min; i <= i_max; ++i) {
1594 num_potential_asc += asc_heights->
pile_count(i);
1604 STATS heights (min_height, max_height + 1);
1605 for (blob_it.mark_cycle_pt(); !blob_it.cycled_list(); blob_it.forward()) {
1606 blob = blob_it.data();
1610 height = (gradient * xcentre + row->
parallel_c() -
1612 if (height >= min_height && height <= max_height)
1613 heights.
add(static_cast<int>(floor(height + 0.5)), 1);
1616 int blob_index = heights.
mode();
1617 int blob_count = heights.
pile_count(blob_index);
1618 float total_fraction =
1620 if (static_cast<float>(blob_count + num_potential_asc) <
1621 xheight_blob_count * total_fraction) {
1624 int descdrop = blob_count > 0 ? -blob_index : 0;
1626 tprintf(
"Descdrop: %d (potential ascenders %d, descenders %d)\n",
1627 descdrop, num_potential_asc, blob_count);
1652 src_count = max_height + 1 - min_height;
1656 for (src_index = 0; src_index < src_count; src_index++) {
1657 pile_count = heights->
pile_count(min_height + src_index);
1658 if (pile_count > 0) {
1659 if (dest_count < maxmodes) {
1660 if (pile_count < least_count) {
1662 least_count = pile_count;
1663 least_index = dest_count;
1665 modes[dest_count++] = min_height + src_index;
1666 }
else if (pile_count >= least_count) {
1667 while (least_index < maxmodes - 1) {
1668 modes[least_index] = modes[least_index + 1];
1673 modes[maxmodes - 1] = min_height + src_index;
1674 if (pile_count == least_count) {
1676 least_index = maxmodes - 1;
1680 for (dest_count = 1; dest_count < maxmodes; dest_count++) {
1681 pile_count = heights->
pile_count(modes[dest_count]);
1682 if (pile_count < least_count) {
1684 least_count = pile_count;
1685 least_index = dest_count;
1703 float ascrise,
float descdrop) {
1706 tprintf(
"correcting row xheight: row->xheight %.4f" 1707 ", row->acrise %.4f row->descdrop %.4f\n",
1710 bool normal_xheight =
1727 (normal_xheight || cap_xheight)) ||
1728 (row_category ==
ROW_UNKNOWN && normal_xheight)) {
1752 if (row->
xheight < xheight + ascrise && row->
xheight > xheight) {
1755 tprintf(
"all caps with irregular xheight\n");
1764 tprintf(
"corrected row->xheight = %.4f, row->acrise = %.4f, row->descdrop" 1769 static int CountOverlaps(
const TBOX& box,
int min_height,
1770 BLOBNBOX_LIST* blobs) {
1772 BLOBNBOX_IT blob_it(blobs);
1773 for (blob_it.mark_cycle_pt(); !blob_it.cycled_list(); blob_it.forward()) {
1800 BLOBNBOX_IT blob_it;
1804 TO_ROW_IT row_it = block->
get_rows();
1809 length = sqrt(1 + gradient * gradient);
1810 g_vec =
FCOORD(1 / length, -gradient / length);
1811 blob_rotation =
FCOORD(rotation.
x(), -rotation.
y());
1812 blob_rotation.
rotate(g_vec);
1813 for (row_it.mark_cycle_pt(); !row_it.cycled_list(); row_it.forward()) {
1814 row = row_it.data();
1817 for (blob_it.mark_cycle_pt(); !blob_it.cycled_list();
1818 blob_it.forward()) {
1819 blob = blob_it.data();
1827 rotated_blob, static_cast<inT16>(row->
intercept()),
1832 under_it.add_after_then_move(blob_it.extract());
1834 tprintf(
"Underlined blob at:");
1835 rotated_blob->bounding_box().print();
1839 }
else if (CountOverlaps(blob->
bounding_box(), min_blob_height,
1842 large_it.add_after_then_move(blob_it.extract());
1844 tprintf(
"Large blob overlaps %d blobs at:",
1845 CountOverlaps(blob_box, min_blob_height,
1850 delete rotated_blob;
1868 #ifndef GRAPHICS_DISABLED 1875 BLOBNBOX_IT blob_it;
1876 BLOBNBOX_IT start_it;
1877 TO_ROW_IT row_it = block->
get_rows ();
1879 #ifndef GRAPHICS_DISABLED 1883 blob_rotation =
FCOORD (rotation.
x (), -rotation.
y ());
1884 for (row_it.mark_cycle_pt (); !row_it.cycled_list (); row_it.forward ()) {
1886 blob_it.set_to_list (row_it.data ()->blob_list ());
1887 for (blob_it.mark_cycle_pt (); !blob_it.cycled_list ();
1888 blob_it.forward ()) {
1889 blob = blob_it.data ();
1902 if (!blob_it.at_last ()) {
1903 nextblob = blob_it.data_relative(1);
1906 blob->
merge(nextblob);
1913 blob->
chop (&start_it, &blob_it,
1919 #ifndef GRAPHICS_DISABLED 1924 for (blob_it.mark_cycle_pt (); !blob_it.cycled_list ();
1925 blob_it.forward ()) {
1926 blob = blob_it.data ();
1928 blob_box.
rotate (rotation);
1931 blob_box.
right (), blob_box.
top ());
1955 #ifndef GRAPHICS_DISABLED 1958 TO_ROW_IT row_it = block->
get_rows ();
1960 row_it.move_to_first ();
1961 for (row_it.mark_cycle_pt (); !row_it.cycled_list (); row_it.forward ()) {
1962 if (row_it.data ()->blob_list ()->empty ())
1963 delete row_it.extract ();
1967 #ifndef GRAPHICS_DISABLED 1970 for (row_it.mark_cycle_pt (); !row_it.cycled_list (); row_it.forward ()) {
1972 block_edge, colour, rotation);
1997 for (blob_it.mark_cycle_pt(); !blob_it.cycled_list(); blob_it.forward()) {
1998 if (!blob_it.data()->joined_to_prev()) {
1999 const TBOX& box = blob_it.data()->bounding_box();
2007 error = lms.
Fit(&gradient, &c);
2023 #ifndef GRAPHICS_DISABLED 2026 TO_ROW_IT row_it = block->
get_rows ();
2028 row_it.move_to_first ();
2029 for (row_it.mark_cycle_pt (); !row_it.cycled_list (); row_it.forward ()) {
2030 if (row_it.data ()->blob_list ()->empty ())
2031 delete row_it.extract ();
2036 #ifndef GRAPHICS_DISABLED 2039 for (row_it.mark_cycle_pt (); !row_it.cycled_list ();
2040 row_it.forward ()) {
2041 row_it.data ()->baseline.plot (
to_win, colour);
2048 make_old_baselines(block, testing_on, gradient);
2050 #ifndef GRAPHICS_DISABLED 2053 for (row_it.mark_cycle_pt (); !row_it.cycled_list (); row_it.forward ()) {
2054 row_it.data ()->baseline.plot (
to_win, colour);
2085 xstarts[1] = xstarts[segments];
2087 coeffs = (
double *)
alloc_mem (3 *
sizeof (
double));
2089 coeffs[1] = row->
line_m ();
2090 coeffs[2] = row->
line_c ();
2122 BLOBNBOX_IT blob_it = row->
blob_list ();
2123 BLOBNBOX_IT new_it = blob_it;
2126 needs_curve =
FALSE;
2128 xstarts[0] = box.
left ();
2130 blobcount = row->
blob_list ()->length ();
2132 tprintf (
"Segmenting baseline of %d blobs at (%d,%d)\n",
2136 blob_it.move_to_last ();
2137 box = blob_it.data ()->bounding_box ();
2138 xstarts[1] = box.
right ();
2142 new_it.mark_cycle_pt ();
2145 middle = (new_box.
left () + new_box.
right ()) / 2.0;
2148 yshifts.
add (yshift, blobindex);
2149 if (new_it.cycled_list ()) {
2150 xstarts[1] = new_box.
right ();
2171 xstarts[segments++] = box.
left ();
2177 middle = (new_box.
left () + new_box.
right ()) / 2.0;
2179 yshifts.
add (yshift, blobindex);
2183 while (!new_it.cycled_list ());
2185 xstarts[segments] = new_box.
right ();
2188 xstarts[--segments] = new_box.
right ();
2191 tprintf (
"Made %d segments on row at (%d,%d)\n",
2214 int blobs_per_segment;
2218 BLOBNBOX_IT blob_it = row->
blob_list ();
2219 BLOBNBOX_IT new_it = blob_it;
2226 xstarts[0] = box.
left ();
2228 while (!blob_it.at_first ()) {
2235 blobs_per_segment = blobcount / segments;
2236 coeffs = (
double *)
alloc_mem (segments * 3 *
sizeof (
double));
2239 (
"Linear splining baseline of %d blobs at (%d,%d), into %d segments of %d blobs\n",
2240 blobcount, box.
left (), box.
bottom (), segments, blobs_per_segment);
2242 for (index2 = 0; index2 < blobs_per_segment / 2; index2++)
2247 blobindex += blobs_per_segment;
2249 while (index1 < blobindex || (segment == segments && index1 < blobcount)) {
2251 int middle = (box.
left() + box.
right()) / 2;
2254 if (index1 == blobindex - blobs_per_segment / 2
2255 || index1 == blobcount - 1) {
2256 xstarts[segment] = box.
left ();
2260 coeffs[segment * 3 - 3] = 0;
2261 coeffs[segment * 3 - 2] = b;
2262 coeffs[segment * 3 - 1] = c;
2264 if (segment > segments)
2267 blobindex += blobs_per_segment;
2269 while (index2 < blobindex || (segment == segments && index2 < blobcount)) {
2271 int middle = (new_box.
left() + new_box.
right()) / 2;
2274 if (index2 == blobindex - blobs_per_segment / 2
2275 || index2 == blobcount - 1) {
2276 xstarts[segment] = new_box.
left ();
2280 coeffs[segment * 3 - 3] = 0;
2281 coeffs[segment * 3 - 2] = b;
2282 coeffs[segment * 3 - 1] = c;
2285 while (segment <= segments);
2300 BOOL8 reject_misses,
2301 BOOL8 make_new_rows,
2307 float g_length = 1.0f;
2312 float smooth_factor;
2319 BLOBNBOX_IT blob_it = &block->
blobs;
2320 TO_ROW_IT row_it = block->
get_rows ();
2325 if (gradient != NULL)
2326 g_length = sqrt (1 + *gradient * *gradient);
2327 #ifndef GRAPHICS_DISABLED 2333 smooth_factor = 1.0;
2335 row_count = row_it.length ();
2336 if (!blob_it.empty ()) {
2337 left_x = blob_it.data ()->bounding_box ().left ();
2343 for (blob_it.mark_cycle_pt (); !blob_it.cycled_list (); blob_it.forward ()) {
2344 blob = blob_it.data ();
2345 if (gradient != NULL) {
2350 && last_x - left_x > block->
line_size * 2
2354 / (last_x - left_x);
2360 #ifndef GRAPHICS_DISABLED 2364 if (!row_it.empty ()) {
2365 for (row_it.move_to_first ();
2366 !row_it.at_last () && row_it.data ()->min_y () > top;
2368 row = row_it.data ();
2369 if (row->
min_y () <= top && row->
max_y () >= bottom) {
2377 if (overlap_result ==
NEW_ROW && !reject_misses)
2382 if (!make_new_rows) {
2383 near_dist = row_it.data_relative (-1)->min_y () - top;
2385 if (bottom < row->min_y ()) {
2386 if (row->
min_y () - bottom <=
2394 else if (near_dist > 0
2395 && near_dist < bottom - row->max_y ()) {
2397 dest_row = row_it.data ();
2398 if (dest_row->
min_y () - bottom <=
2406 if (top - row->
max_y () <=
2417 if (overlap_result ==
ASSIGN)
2418 dest_row->
add_blob (blob_it.extract (), top, bottom,
2420 if (overlap_result ==
NEW_ROW) {
2421 if (make_new_rows && top - bottom < block->max_blob_size) {
2423 new TO_ROW (blob_it.extract (), top, bottom,
2426 if (bottom > row_it.data ()->min_y ())
2427 row_it.add_before_then_move (dest_row);
2430 row_it.add_after_then_move (dest_row);
2439 else if (make_new_rows && top - bottom < block->max_blob_size) {
2444 row_it.add_after_then_move(dest_row);
2451 if (overlap_result !=
REJECT) {
2452 tprintf(
"Test blob assigned to row at (%g,%g) on pass %d\n",
2456 tprintf(
"Test blob assigned to no row on pass %d\n", pass);
2459 if (overlap_result !=
REJECT) {
2460 while (!row_it.at_first() &&
2461 row_it.data()->min_y() > row_it.data_relative(-1)->min_y()) {
2462 row = row_it.extract();
2464 row_it.add_before_then_move(row);
2466 while (!row_it.at_last() &&
2467 row_it.data ()->min_y() < row_it.data_relative (1)->min_y()) {
2468 row = row_it.extract();
2471 row_it.add_after_then_move(row);
2473 BLOBNBOX_IT added_blob_it(dest_row->
blob_list());
2474 added_blob_it.move_to_last();
2475 TBOX prev_box = added_blob_it.data_relative(-1)->bounding_box();
2476 if (dest_row->
blob_list()->singleton() ||
2478 block_skew = (1 - smooth_factor) * block_skew
2484 for (row_it.mark_cycle_pt(); !row_it.cycled_list(); row_it.forward()) {
2485 if (row_it.data()->blob_list()->empty())
2486 delete row_it.extract();
2507 float merge_top, merge_bottom;
2511 BLOBNBOX_IT blob_it;
2514 row = row_it->data ();
2515 bestover = top - bottom;
2516 if (top > row->
max_y ())
2517 bestover -= top - row->
max_y ();
2518 if (bottom < row->min_y ())
2520 bestover -= row->
min_y () - bottom;
2522 tprintf(
"Test blob y=(%g,%g), row=(%f,%f), size=%g, overlap=%f\n",
2523 bottom, top, row->
min_y(), row->
max_y(), rowsize, bestover);
2527 if (!row_it->at_last ()) {
2529 test_row = row_it->data ();
2530 if (test_row->
min_y () <= top && test_row->
max_y () >= bottom) {
2532 test_row->
max_y () >
2535 test_row->
min_y () <
2537 if (merge_top - merge_bottom <= rowsize) {
2539 tprintf (
"Merging rows at (%g,%g), (%g,%g)\n",
2543 test_row->
set_limits (merge_bottom, merge_top);
2544 blob_it.set_to_list (test_row->
blob_list ());
2545 blob_it.add_list_after (row->
blob_list ());
2547 row_it->backward ();
2548 delete row_it->extract ();
2552 overlap = top - bottom;
2553 if (top > test_row->
max_y ())
2554 overlap -= top - test_row->
max_y ();
2555 if (bottom < test_row->min_y ())
2556 overlap -= test_row->
min_y () - bottom;
2557 if (bestover >= rowsize - 1 && overlap >= rowsize - 1) {
2560 if (overlap > bestover) {
2565 tprintf(
"Test blob y=(%g,%g), row=(%f,%f), size=%g, overlap=%f->%f\n",
2566 bottom, top, test_row->
min_y(), test_row->
max_y(),
2567 rowsize, overlap, bestover);
2572 while (!row_it->at_last ()
2573 && test_row->
min_y () <= top && test_row->
max_y () >= bottom);
2574 while (row_it->data () != row)
2575 row_it->backward ();
2593 const void *item2) {
2615 const void *item2) {
2637 const void *item2) {
2659 int num_repeated_sets = 0;
2660 if (!box_it.empty()) {
2663 int repeat_length = 1;
2666 BLOBNBOX_IT test_it(box_it);
2667 for (test_it.forward(); !test_it.at_first();) {
2668 bblob = test_it.data();
2672 bblob = test_it.data();
2681 num_repeated_sets++;
2682 for (; repeat_length > 0; box_it.forward(), --repeat_length) {
2683 bblob = box_it.data();
2690 }
while (!box_it.at_first());
void DrawTo(int x, int y)
double textord_spline_outlier_fraction
void correct_row_xheight(TO_ROW *row, float xheight, float ascrise, float descdrop)
double textord_min_linesize
const TBOX & bounding_box() const
void rotate(const FCOORD &vec)
bool textord_test_landscape
bool textord_new_initial_xheight
void delete_non_dropout_rows(TO_BLOCK *block, float gradient, FCOORD rotation, inT32 block_edge, BOOL8 testing_on)
bool textord_debug_xheights
ScrollView * create_to_win(ICOORD page_tr)
void expand_rows(ICOORD page_tr, TO_BLOCK *block, float gradient, FCOORD rotation, inT32 block_edge, BOOL8 testing_on)
double textord_occupancy_threshold
bool textord_fix_xheight_bug
void set_limits(float new_min, float new_max)
void separate_underlines(TO_BLOCK *block, float gradient, FCOORD rotation, BOOL8 testing_on)
inT32 compute_height_modes(STATS *heights, inT32 min_height, inT32 max_height, inT32 *modes, inT32 maxmodes)
void fit_parallel_lms(float gradient, TO_ROW *row)
int textord_max_blob_overlaps
void make_spline_rows(TO_BLOCK *block, float gradient, BOOL8 testing_on)
void draw_occupation(inT32 xleft, inT32 ybottom, inT32 min_y, inT32 max_y, inT32 occupation[], inT32 thresholds[])
void compute_line_occupation(TO_BLOCK *block, float gradient, inT32 min_y, inT32 max_y, inT32 *occupation, inT32 *deltas)
inT32 compute_row_descdrop(TO_ROW *row, float gradient, int xheight_blob_count, STATS *asc_heights)
int num_repeated_sets() const
float believability() const
void SetCursor(int x, int y)
BOOL8 test_underline(BOOL8 testing_on, C_BLOB *blob, inT16 baseline, inT16 xheight)
double textord_linespace_iqrlimit
bool textord_show_final_blobs
void get_min_max_xheight(int block_linesize, int *min_height, int *max_height)
bool textord_show_parallel_rows
float MakeRowFromSubBlobs(TO_BLOCK *block, C_BLOB *blob, TO_ROW_IT *row_it)
int textord_skewsmooth_offset
void add(inT32 value, inT32 count)
double Fit(ICOORD *pt1, ICOORD *pt2)
bool textord_straight_baselines
void Add(const ICOORD &pt)
int row_spacing_order(const void *item1, const void *item2)
int textord_spline_minblobs
int textord_min_blobs_in_row
void set_parallel_line(float gradient, float new_c, float new_error)
BOOL8 segment_baseline(TO_ROW *row, TO_BLOCK *block, inT32 &segments, inT32 xstarts[])
bool textord_parallel_baselines
double ConstrainedFit(const FCOORD &direction, double min_dist, double max_dist, bool debug, ICOORD *line_pt)
BlobTextFlowType flow() const
ICOORD step(int index) const
void add_blob(BLOBNBOX *blob, float top, float bottom, float row_size)
void cleanup_rows_making(ICOORD page_tr, TO_BLOCK *block, float gradient, FCOORD rotation, inT32 block_edge, BOOL8 testing_on)
void compute_occupation_threshold(inT32 low_window, inT32 high_window, inT32 line_count, inT32 *occupation, inT32 *thresholds)
C_OUTLINE_LIST * out_list()
double textord_ascx_ratio_min
double textord_xheight_mode_fraction
void compute_row_stats(TO_BLOCK *block, BOOL8 testing_on)
TBOX deskew_block_coords(TO_BLOCK *block, float gradient)
void add(float value, inT32 key)
int row_y_order(const void *item1, const void *item2)
bool within_error_margin(float test, float num, float margin)
bool textord_show_initial_rows
BLOBNBOX_LIST * blob_list()
int blob_x_order(const void *item1, const void *item2)
double textord_chop_width
double textord_excess_blobsize
inT32 choose_nth_item(inT32 index, float *array, inT32 count)
void assign_blobs_to_rows(TO_BLOCK *block, float *gradient, int pass, BOOL8 reject_misses, BOOL8 make_new_rows, BOOL8 drawing_skew)
void make_initial_textrows(ICOORD page_tr, TO_BLOCK *block, FCOORD rotation, BOOL8 testing_on)
bool major_x_overlap(const TBOX &box) const
double * linear_spline_baseline(TO_ROW *row, TO_BLOCK *block, inT32 &segments, inT32 xstarts[])
void adjust_row_limits(TO_BLOCK *block)
void compute_page_skew(TO_BLOCK_LIST *blocks, float &page_m, float &page_err)
bool textord_biased_skewcalc
double textord_expansion_factor
void rotate(const FCOORD vec)
void plot_to_row(TO_ROW *row, ScrollView::Color colour, FCOORD rotation)
BLOBNBOX_LIST noise_blobs
bool joined_to_prev() const
double textord_descx_ratio_min
bool contains(const FCOORD pt) const
static const double kXHeightCapRatio
bool textord_show_expanded_rows
void compute_block_xheight(TO_BLOCK *block, float gradient)
void * alloc_mem(inT32 count)
int textord_lms_line_trials
int textord_spline_medianwin
inT16 x() const
access function
int textord_skewsmooth_offset2
void set_num_repeated_sets(int num_sets)
BLOBNBOX_LIST large_blobs
void mark_repeated_chars(TO_ROW *row)
void make_baseline_spline(TO_ROW *row, TO_BLOCK *block)
bool textord_interpolating_skew
bool rep_chars_marked() const
void fill_heights(TO_ROW *row, float gradient, int min_height, int max_height, STATS *heights, STATS *floating_heights)
EXTERN bool textord_oldbl_debug
void fit_parallel_rows(TO_BLOCK *block, float gradient, FCOORD rotation, inT32 block_edge, BOOL8 testing_on)
double textord_ascheight_mode_fraction
C_BLOB * crotate_cblob(C_BLOB *blob, FCOORD rotation)
bool textord_show_final_rows
static const double kAscenderFraction
double textord_underline_width
double textord_descheight_mode_fraction
void Rectangle(int x1, int y1, int x2, int y2)
void error(const char *caller, TessErrorLogCode action, const char *format,...) const
float make_rows(ICOORD page_tr, TO_BLOCK_LIST *port_blocks)
ROW_CATEGORY get_row_category(const TO_ROW *row)
double textord_ascx_ratio_max
void vigorous_noise_removal(TO_BLOCK *block)
BOOL8 find_best_dropout_row(TO_ROW *row, inT32 distance, float dist_limit, inT32 line_index, TO_ROW_IT *row_it, BOOL8 testing_on)
inT32 pile_count(inT32 value) const
BLOBNBOX_LIST small_blobs
void set_line(float new_m, float new_c, float new_error)
static const double kXHeightFraction
FCOORD classify_rotation() const
void fit_lms_line(TO_ROW *row)
double textord_descx_ratio_max
const int kMinLeaderCount
double textord_xheight_error_margin
float make_single_row(ICOORD page_tr, bool allow_sub_blobs, TO_BLOCK *block, TO_BLOCK_LIST *blocks)
void CheckInverseFlagAndDirection()
const ICOORD & start_pos() const
bool major_overlap(const TBOX &box) const
TBOX box_next_pre_chopped(BLOBNBOX_IT *it)
bool textord_fix_makerow_bug
void set_repeated_set(int set_id)
static C_BLOB * FakeBlob(const TBOX &box)
OVERLAP_STATE most_overlapping_row(TO_ROW_IT *row_it, TO_ROW *&best_row, float top, float bottom, float rowsize, BOOL8 testing_blob)
float initial_min_y() const
static C_OUTLINE * deep_copy(const C_OUTLINE *src)
void pre_associate_blobs(ICOORD page_tr, TO_BLOCK *block, FCOORD rotation, BOOL8 testing_on)
static const double kDescenderFraction
void chop(BLOBNBOX_IT *start_it, BLOBNBOX_IT *blob_it, FCOORD rotation, float xheight)
double textord_width_limit
void compute_row_xheight(TO_ROW *row, const FCOORD &rotation, float gradient, int block_line_size)
#define double_VAR(name, val, comment)
EXTERN ScrollView * to_win
void free_mem(void *oldchunk)
void merge(BLOBNBOX *nextblob)
#define BOOL_VAR(name, val, comment)
void plot_parallel_row(TO_ROW *row, float gradient, inT32 left, ScrollView::Color colour, FCOORD rotation)
bool textord_single_height_mode
double textord_min_blob_height_fraction
int compute_xheight_from_modes(STATS *heights, STATS *floating_heights, bool cap_only, int min_height, int max_height, float *xheight, float *ascrise)
double textord_spline_shift_fraction
void compute_dropout_distances(inT32 *occupation, inT32 *thresholds, inT32 line_count)
void bounding_box(ICOORD &bottom_left, ICOORD &top_right) const
get box
bool textord_old_baselines
#define INT_VAR(name, val, comment)
inT16 y() const
access_function