42 #include "config_auto.h"
69 "Max number of blobs a big blob can overlap");
72 "Fraction of line spacing for quad");
74 "Fraction of line spacing for outlier");
81 "Factor to expand rows by in expand_rows");
86 "New row made if blob makes row this big");
90 "Min blob height/top to include blob top into xheight stats");
92 "Min pile height to make xheight");
94 "Min pile height to make ascheight");
96 "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()) {
149 C_OUTLINE_LIST outlines;
150 C_OUTLINE_IT ol_it(&outlines);
151 ol_it.add_after_then_move(outline);
154 bb_it.add_after_then_move(bbox);
157 return MakeRowFromBlobs(block->
line_size, &bb_it, row_it);
168 BLOBNBOX_IT blob_it = &block->
blobs;
169 TO_ROW_IT row_it = block->
get_rows();
175 if (block->
blobs.singleton()) {
176 blob_it.move_to_first();
181 MakeRowFromBlobs(block->
line_size, &blob_it, &row_it);
183 for (row_it.mark_cycle_pt(); !row_it.cycled_list(); row_it.forward())
200 TO_BLOCK_IT block_it;
202 block_it.set_to_list(port_blocks);
203 for (block_it.mark_cycle_pt(); !block_it.cycled_list();
209 block_it.set_to_list(port_blocks);
210 for (block_it.mark_cycle_pt(); !block_it.cycled_list(); block_it.forward()) {
212 block_it.data()->block->bounding_box().left(),
220 void Textord::fit_rows(
float gradient,
ICOORD page_tr, TO_BLOCK_LIST *blocks) {
221 TO_BLOCK_IT block_it(blocks);
222 for (block_it.mark_cycle_pt(); !block_it.cycled_list(); block_it.forward()) {
223 cleanup_rows_fitting(page_tr, block_it.data(), gradient,
FCOORD(1.0
f, 0.0
f),
224 block_it.data()->block->bounding_box().left(),
242 TO_ROW_IT row_it = block->
get_rows ();
244 #ifndef GRAPHICS_DISABLED
254 row_it.move_to_first ();
255 for (row_it.mark_cycle_pt (); !row_it.cycled_list (); row_it.forward ())
257 #ifndef GRAPHICS_DISABLED
260 for (row_it.mark_cycle_pt (); !row_it.cycled_list (); row_it.forward ()) {
281 for (blob_it.mark_cycle_pt(); !blob_it.cycled_list(); blob_it.forward()) {
282 const TBOX& box = blob_it.data()->bounding_box();
285 double error = lms.
Fit(&m, &c);
297 TO_BLOCK_LIST *blocks,
308 TO_BLOCK_IT block_it = blocks;
313 for (block_it.mark_cycle_pt (); !block_it.cycled_list ();
314 block_it.forward ()) {
315 POLY_BLOCK* pb = block_it.data()->block->poly_block();
318 row_count += block_it.data ()->get_rows ()->length ();
320 row_it.set_to_list (block_it.data ()->get_rows ());
321 for (row_it.mark_cycle_pt (); !row_it.cycled_list (); row_it.forward ())
322 blob_count += row_it.data ()->blob_list ()->length ();
324 if (row_count == 0) {
329 gradients = (
float *)
alloc_mem (blob_count *
sizeof (
float));
331 errors = (
float *)
alloc_mem (blob_count *
sizeof (
float));
332 if (gradients ==
NULL || errors ==
NULL)
336 for (block_it.mark_cycle_pt (); !block_it.cycled_list ();
337 block_it.forward ()) {
338 POLY_BLOCK* pb = block_it.data()->block->poly_block();
341 row_it.set_to_list (block_it.data ()->get_rows ());
342 for (row_it.mark_cycle_pt (); !row_it.cycled_list (); row_it.forward ()) {
343 row = row_it.data ();
344 blob_count = row->
blob_list ()->length ();
349 blob_count /= row_err;
350 for (blob_count /= row_err; blob_count > 0; blob_count--) {
351 gradients[row_index] = row->
line_m ();
358 gradients[row_index] = row->
line_m ();
364 if (row_index == 0) {
366 for (block_it.mark_cycle_pt (); !block_it.cycled_list ();
367 block_it.forward ()) {
368 POLY_BLOCK* pb = block_it.data()->block->poly_block();
371 row_it.set_to_list (block_it.data ()->get_rows ());
372 for (row_it.mark_cycle_pt (); !row_it.cycled_list ();
374 row = row_it.data ();
375 gradients[row_index] = row->
line_m ();
381 row_count = row_index;
383 gradients, row_count);
384 page_m = gradients[row_index];
387 page_err = errors[row_index];
407 (overlap * 2 < ibox.
width() && overlap < dotbox.
width()))
417 const double kHeightFraction = 0.6;
418 double target_height =
MIN(dotbox.
bottom(), ibox.
top());
420 target_height *= kHeightFraction;
421 int left_min = dotbox.
left() - dotbox.
width();
422 int middle = (dotbox.
left() + dotbox.
right())/2;
423 int right_max = dotbox.
right() + dotbox.
width();
428 bool found_left =
false;
429 bool found_right =
false;
430 bool in_left =
false;
431 bool in_right =
false;
433 C_OUTLINE_IT o_it = blob->
out_list();
434 for (o_it.mark_cycle_pt(); !o_it.cycled_list(); o_it.forward()) {
438 for (
int step = 0; step < length; pos += outline->
step(step++)) {
441 if (x >= left_min && x < middle && !found_left) {
444 if (y > left_maxy) left_maxy = y;
445 if (y < left_miny) left_miny = y;
447 left_maxy = left_miny = y;
450 }
else if (in_left) {
452 if (left_maxy - left_miny > target_height) {
459 if (x <= right_max && x > middle && !found_right) {
462 if (y > right_maxy) right_maxy = y;
463 if (y < right_miny) right_miny = y;
465 right_maxy = right_miny = y;
468 }
else if (in_right) {
470 if (right_maxy - right_miny > target_height) {
482 static void vigorous_noise_removal(
TO_BLOCK* block) {
483 TO_ROW_IT row_it = block->
get_rows ();
484 for (row_it.mark_cycle_pt (); !row_it.cycled_list (); row_it.forward ()) {
485 TO_ROW* row = row_it.data();
489 for (b_it.mark_cycle_pt(); !b_it.cycled_list(); b_it.forward()) {
494 STATS hstats(0, max_height + 1);
495 for (b_it.mark_cycle_pt(); !b_it.cycled_list(); b_it.forward()) {
498 if (height >= kMinSize)
501 float xheight = hstats.median();
504 for (b_it.mark_cycle_pt(); !b_it.cycled_list(); b_it.forward()) {
507 if (box.
height() < kNoiseSize * xheight) {
510 if (dot_of_i(blob, prev, row))
513 if (!b_it.at_last()) {
514 BLOBNBOX* next = b_it.data_relative(1);
515 if (dot_of_i(blob, next, row))
520 delete blob->
cblob();
521 delete b_it.extract();
543 BLOBNBOX_IT blob_it = &block->
blobs;
544 TO_ROW_IT row_it = block->
get_rows ();
546 #ifndef GRAPHICS_DISABLED
563 expand_rows(page_tr, block, gradient, rotation, block_edge, testing_on);
564 blob_it.set_to_list (&block->
blobs);
565 row_it.set_to_list (block->
get_rows ());
566 for (row_it.mark_cycle_pt (); !row_it.cycled_list (); row_it.forward ())
567 blob_it.add_list_after (row_it.data ()->blob_list ());
571 blob_it.set_to_list (&block->
blobs);
575 blob_it.set_to_list (&block->
blobs);
584 void Textord::cleanup_rows_fitting(
ICOORD page_tr,
590 BLOBNBOX_IT blob_it = &block->
blobs;
591 TO_ROW_IT row_it = block->
get_rows();
593 #ifndef GRAPHICS_DISABLED
599 for (row_it.mark_cycle_pt(); !row_it.cycled_list(); row_it.forward())
603 vigorous_noise_removal(block);
611 #ifndef GRAPHICS_DISABLED
620 make_spline_rows(block,
633 compute_block_xheight(block, gradient);
636 #ifndef GRAPHICS_DISABLED
639 for (row_it.mark_cycle_pt(); !row_it.cycled_list(); row_it.forward()) {
641 block_edge, colour, rotation);
653 tprintf (
"%d blobs discarded as noise\n", block->
blobs.length ());
685 TO_ROW_IT row_it = block->
get_rows ();
686 BLOBNBOX_IT blob_it = &block->
blobs;
688 if (row_it.length () == 0)
693 min_y = block_box.
bottom () - 1;
694 max_y = block_box.
top () + 1;
695 for (row_it.mark_cycle_pt (); !row_it.cycled_list (); row_it.forward ()) {
696 line_index = (
inT32) floor (row_it.data ()->intercept ());
697 if (line_index <= min_y)
698 min_y = line_index - 1;
699 if (line_index >= max_y)
700 max_y = line_index + 1;
702 line_count = max_y - min_y + 1;
707 if (deltas ==
NULL || occupation ==
NULL)
718 max_y - min_y + 1, occupation, deltas);
719 #ifndef GRAPHICS_DISABLED
725 for (row_it.mark_cycle_pt (); !row_it.cycled_list (); row_it.forward ()) {
726 row = row_it.data ();
728 distance = deltas[line_index - min_y];
730 line_index, &row_it, testing_on)) {
731 #ifndef GRAPHICS_DISABLED
736 blob_it.add_list_after (row_it.data ()->blob_list ());
737 delete row_it.extract ();
740 for (row_it.mark_cycle_pt (); !row_it.cycled_list (); row_it.forward ()) {
741 blob_it.add_list_after (row_it.data ()->blob_list ());
770 tprintf (
"Row at %g(%g), dropout dist=%d,",
774 abs_dist = -distance;
780 if (abs_dist > dist_limit) {
782 tprintf (
" too far - deleting\n");
786 if ((distance < 0 && !row_it->at_last ())
787 || (distance >= 0 && !row_it->at_first ())) {
788 row_offset = row_inc;
790 next_row = row_it->data_relative (row_offset);
793 && next_index < line_index
794 && next_index > line_index + distance + distance)
796 && next_index > line_index
797 && next_index < line_index + distance + distance)) {
799 tprintf (
" nearer neighbour (%d) at %g\n",
800 line_index + distance - next_index,
805 else if (next_index == line_index
806 || next_index == line_index + distance + distance) {
809 tprintf (
" equal but more believable at %g (%g/%g)\n",
817 row_offset += row_inc;
819 while ((next_index == line_index
820 || next_index == line_index + distance + distance)
821 && row_offset < row_it->length ());
843 TO_ROW_IT row_it = block->
get_rows ();
848 length = sqrt (gradient * gradient + 1);
849 rotation =
FCOORD (1 / length, -gradient / length);
850 for (row_it.mark_cycle_pt (); !row_it.cycled_list (); row_it.forward ()) {
851 row = row_it.data ();
853 for (blob_it.mark_cycle_pt (); !blob_it.cycled_list ();
854 blob_it.forward ()) {
855 blob = blob_it.data ();
857 blob_box.
rotate (rotation);
885 TO_ROW_IT row_it = block->
get_rows ();
892 line_count = max_y - min_y + 1;
893 length = sqrt (gradient * gradient + 1);
894 rotation =
FCOORD (1 / length, -gradient / length);
895 for (line_index = 0; line_index < line_count; line_index++)
896 deltas[line_index] = 0;
897 for (row_it.mark_cycle_pt (); !row_it.cycled_list (); row_it.forward ()) {
898 row = row_it.data ();
900 for (blob_it.mark_cycle_pt (); !blob_it.cycled_list ();
901 blob_it.forward ()) {
902 blob = blob_it.data ();
904 blob_box.
rotate (rotation);
905 top = blob_box.
top ();
906 bottom = blob_box.
bottom ();
909 if ((
inT32) floor (bottom) < min_y
910 || (
inT32) floor (bottom) - min_y >= line_count)
915 index = (
inT32) floor (bottom) - min_y;
916 deltas[index] += width;
917 if ((
inT32) floor (top) < min_y
918 || (
inT32) floor (top) - min_y >= line_count)
920 "Bad y coord of top, " INT32FORMAT
"(" INT32FORMAT
","
921 INT32FORMAT
")\n", (
inT32) floor (top), min_y, max_y);
922 index = (
inT32) floor (top) - min_y;
923 deltas[index] -= width;
926 occupation[0] = deltas[0];
927 for (line_index = 1; line_index < line_count; line_index++)
928 occupation[line_index] = occupation[line_index - 1] + deltas[line_index];
955 if (low_window + high_window < line_count) {
956 for (sum = 0, high_index = 0; high_index < low_window; high_index++)
957 sum += occupation[high_index];
958 for (low_index = 0; low_index < high_window; low_index++, high_index++)
959 sum += occupation[high_index];
960 min_occ = occupation[0];
962 for (test_index = 1; test_index < high_index; test_index++) {
963 if (occupation[test_index] <= min_occ) {
964 min_occ = occupation[test_index];
965 min_index = test_index;
968 for (line_index = 0; line_index < low_window; line_index++)
969 thresholds[line_index] = (sum - min_occ) / divisor + min_occ;
971 for (low_index = 0; high_index < line_count; low_index++, high_index++) {
972 sum -= occupation[low_index];
973 sum += occupation[high_index];
974 if (occupation[high_index] <= min_occ) {
976 min_occ = occupation[high_index];
977 min_index = high_index;
980 if (min_index <= low_index) {
981 min_occ = occupation[low_index + 1];
982 min_index = low_index + 1;
983 for (test_index = low_index + 2; test_index <= high_index;
985 if (occupation[test_index] <= min_occ) {
986 min_occ = occupation[test_index];
988 min_index = test_index;
992 thresholds[line_index++] = (sum - min_occ) / divisor + min_occ;
996 min_occ = occupation[0];
998 for (sum = 0, low_index = 0; low_index < line_count; low_index++) {
999 if (occupation[low_index] < min_occ) {
1000 min_occ = occupation[low_index];
1001 min_index = low_index;
1003 sum += occupation[low_index];
1007 for (; line_index < line_count; line_index++)
1008 thresholds[line_index] = (sum - min_occ) / divisor + min_occ;
1027 inT32 prev_threshold;
1029 distance = -line_count;
1034 prev_threshold = thresholds[line_index];
1036 thresholds[line_index] = distance;
1039 while (line_index < line_count
1040 && (occupation[line_index] < thresholds[line_index]
1041 || occupation[line_index - 1] >= prev_threshold));
1042 if (line_index < line_count) {
1043 back_index = line_index - 1;
1045 while (next_dist < -distance && back_index >= 0) {
1046 thresholds[back_index] = next_dist;
1054 while (line_index < line_count);
1073 BOOL8 swallowed_row;
1075 float y_bottom, y_top;
1079 BLOBNBOX_IT blob_it = &block->
blobs;
1080 TO_ROW_IT row_it = block->
get_rows ();
1082 #ifndef GRAPHICS_DISABLED
1091 if (block->
get_rows ()->length () == 0)
1097 if (block->
get_rows ()->length () == 0)
1106 row_it.move_to_last ();
1108 row = row_it.data ();
1109 y_max = row->
max_y ();
1110 y_min = row->
min_y ();
1116 if (y_min > y_bottom) {
1118 tprintf(
"Expanding bottom of row at %f from %f to %f\n",
1121 swallowed_row =
TRUE;
1122 while (swallowed_row && !row_it.at_last ()) {
1123 swallowed_row =
FALSE;
1125 test_row = row_it.data_relative (1);
1127 if (test_row->
max_y () > y_bottom) {
1128 if (test_row->
min_y () > y_bottom) {
1132 #ifndef GRAPHICS_DISABLED
1140 blob_it.set_to_list (row->
blob_list ());
1141 blob_it.add_list_after (test_row->
blob_list ());
1143 delete row_it.extract ();
1145 swallowed_row =
TRUE;
1147 else if (test_row->
max_y () < y_min) {
1149 y_bottom = test_row->
max_y ();
1151 tprintf(
"Truncating limit to %f due to touching row at %f\n",
1157 tprintf(
"Not expanding limit beyond %f due to touching row at %f\n",
1164 if (y_max < y_top) {
1166 tprintf(
"Expanding top of row at %f from %f to %f\n",
1168 swallowed_row =
TRUE;
1169 while (swallowed_row && !row_it.at_first ()) {
1170 swallowed_row =
FALSE;
1172 test_row = row_it.data_relative (-1);
1173 if (test_row->
min_y () < y_top) {
1174 if (test_row->
max_y () < y_top) {
1178 blob_it.set_to_list (row->
blob_list ());
1179 #ifndef GRAPHICS_DISABLED
1187 blob_it.add_list_after (test_row->
blob_list ());
1189 delete row_it.extract ();
1191 swallowed_row =
TRUE;
1193 else if (test_row->
min_y () < y_max) {
1195 y_top = test_row->
min_y ();
1197 tprintf(
"Truncating limit to %f due to touching row at %f\n",
1203 tprintf(
"Not expanding limit beyond %f due to touching row at %f\n",
1214 while (!row_it.at_last ());
1230 TO_ROW_IT row_it = block->
get_rows ();
1233 tprintf(
"Adjusting row limits for block(%d,%d)\n",
1236 for (row_it.mark_cycle_pt (); !row_it.cycled_list (); row_it.forward ()) {
1237 row = row_it.data ();
1240 tprintf(
"Row at %f has min %f, max %f, size %f\n",
1267 TO_ROW_IT row_it = block->
get_rows ();
1269 inT16 rowcount = row_it.length ();
1277 row_it.move_to_last ();
1279 row = row_it.data ();
1280 if (prev_row !=
NULL) {
1281 rows[rowcount++] = prev_row;
1284 tprintf (
"Row at %g yields spacing of %g\n",
1290 while (!row_it.at_last ());
1295 tprintf (
"Blob based spacing=(%g,%g), offset=%g",
1300 iqr = rows[row_index]->
spacing;
1303 iqr -= rows[row_index]->
spacing;
1306 block->
key_row = rows[row_index];
1308 tprintf (
" row based=%g(%g)", rows[row_index]->spacing, iqr);
1312 if (rows[row_index]->spacing < block->line_spacing
1313 && rows[row_index]->spacing > block->
line_size)
1317 else if (rows[row_index]->spacing > block->
line_spacing)
1322 if (rows[row_index]->spacing < block->line_spacing)
1338 tprintf (
"\nEstimate line size=%g, spacing=%g, offset=%g\n",
1374 void Textord::compute_block_xheight(
TO_BLOCK *block,
float gradient) {
1380 inT32 min_height, max_height;
1381 TO_ROW_IT row_it = block->
get_rows();
1382 if (row_it.empty())
return;
1387 STATS row_asc_xheights(min_height, max_height + 1);
1388 STATS row_asc_ascrise(static_cast<int>(min_height * asc_frac_xheight),
1389 static_cast<int>(max_height * asc_frac_xheight) + 1);
1390 int min_desc_height =
static_cast<int>(min_height * desc_frac_xheight);
1391 int max_desc_height =
static_cast<int>(max_height * desc_frac_xheight);
1392 STATS row_asc_descdrop(min_desc_height, max_desc_height + 1);
1393 STATS row_desc_xheights(min_height, max_height + 1);
1394 STATS row_desc_descdrop(min_desc_height, max_desc_height + 1);
1395 STATS row_cap_xheights(min_height, max_height + 1);
1396 STATS row_cap_floating_xheights(min_height, max_height + 1);
1397 for (row_it.mark_cycle_pt(); !row_it.cycled_list(); row_it.forward()) {
1398 row = row_it.data();
1406 row_asc_xheights.add(static_cast<inT32>(row->
xheight),
1408 row_asc_ascrise.add(static_cast<inT32>(row->
ascrise),
1410 row_asc_descdrop.add(static_cast<inT32>(-row->
descdrop),
1413 row_desc_xheights.add(static_cast<inT32>(row->
xheight),
1415 row_desc_descdrop.add(static_cast<inT32>(-row->
descdrop),
1419 &row_cap_xheights, &row_cap_floating_xheights);
1423 float xheight = 0.0;
1424 float ascrise = 0.0;
1425 float descdrop = 0.0;
1427 if (row_asc_xheights.get_total() > 0) {
1429 xheight = row_asc_xheights.median();
1430 ascrise = row_asc_ascrise.median();
1431 descdrop = -row_asc_descdrop.median();
1432 }
else if (row_desc_xheights.get_total() > 0) {
1434 xheight = row_desc_xheights.median();
1435 descdrop = -row_desc_descdrop.median();
1436 }
else if (row_cap_xheights.get_total() > 0) {
1447 min_height, max_height, &(xheight), &(ascrise));
1455 bool corrected_xheight =
false;
1458 corrected_xheight =
true;
1460 if (corrected_xheight || ascrise <= 0.0) {
1461 ascrise = xheight * asc_frac_xheight;
1463 if (corrected_xheight || descdrop >= 0.0) {
1464 descdrop = -(xheight * desc_frac_xheight);
1469 tprintf(
"Block average xheight=%.4f, ascrise=%.4f, descdrop=%.4f\n",
1470 xheight, ascrise, descdrop);
1473 for (row_it.mark_cycle_pt(); !row_it.cycled_list(); row_it.forward()) {
1486 void Textord::compute_row_xheight(
TO_ROW *row,
1489 int block_line_size) {
1497 int min_height, max_height;
1499 STATS heights(min_height, max_height + 1);
1500 STATS floating_heights(min_height, max_height + 1);
1502 &heights, &floating_heights);
1508 rotation.
y() == 0.0,
1509 min_height, max_height,
1513 row->
descdrop =
static_cast<float>(
1527 int max_height,
STATS *heights,
STATS *floating_heights) {
1534 if (blob_it.empty())
return;
1535 bool has_rep_chars =
1538 blob = blob_it.data();
1547 top -= gradient * xcentre + row->
parallel_c();
1548 if (top >= min_height && top <= max_height) {
1549 heights->
add(static_cast<inT32>(floor(top + 0.5)), 1);
1551 floating_heights->
add(static_cast<inT32>(floor(top + 0.5)), 1);
1559 while (!blob_it.at_first() &&
1560 blob_it.data()->repeated_set() == repeated_set) {
1563 tprintf(
"Skipping repeated char when computing xheight\n");
1568 }
while (!blob_it.at_first());
1588 STATS *heights,
STATS *floating_heights,
bool cap_only,
int min_height,
1589 int max_height,
float *xheight,
float *ascrise) {
1590 int blob_index = heights->
mode();
1591 int blob_count = heights->
pile_count(blob_index);
1593 tprintf(
"min_height=%d, max_height=%d, mode=%d, count=%d, total=%d\n",
1594 min_height, max_height, blob_index, blob_count,
1597 floating_heights->
print();
1599 if (blob_count == 0)
return 0;
1601 bool in_best_pile =
FALSE;
1606 if (cap_only && mode_count > 1)
1610 tprintf(
"found %d modes: ", mode_count);
1611 for (x = 0; x < mode_count; x++)
tprintf(
"%d ", modes[x]);
1615 for (x = 0; x < mode_count - 1; x++) {
1616 if (modes[x] != prev_size + 1)
1617 in_best_pile =
FALSE;
1618 int modes_x_count = heights->
pile_count(modes[x]) -
1621 (in_best_pile || modes_x_count > best_count)) {
1622 for (
int asc = x + 1; asc < mode_count; asc++) {
1624 static_cast<float>(modes[asc]) / static_cast<float>(modes[x]);
1629 if (modes_x_count > best_count) {
1630 in_best_pile =
true;
1631 best_count = modes_x_count;
1634 tprintf(
"X=%d, asc=%d, count=%d, ratio=%g\n",
1635 modes[x], modes[asc]-modes[x], modes_x_count, ratio);
1637 prev_size = modes[x];
1638 *xheight =
static_cast<float>(modes[x]);
1639 *ascrise =
static_cast<float>(modes[asc] - modes[x]);
1644 if (*xheight == 0) {
1651 if (floating_heights->
get_total() > 0) {
1652 for (x = min_height; x < max_height; ++x) {
1655 blob_index = heights->
mode();
1656 for (x = min_height; x < max_height; ++x) {
1660 *xheight =
static_cast<float>(blob_index);
1662 best_count = heights->
pile_count(blob_index);
1664 tprintf(
"Single mode xheight set to %g\n", *xheight);
1666 tprintf(
"Multi-mode xheight set to %g, asc=%g\n", *xheight, *ascrise);
1684 int xheight_blob_count,
STATS *asc_heights) {
1688 i_min =
static_cast<int>(
1695 int num_potential_asc = 0;
1696 for (
int i = i_min; i <= i_max; ++i) {
1697 num_potential_asc += asc_heights->
pile_count(i);
1707 STATS heights (min_height, max_height + 1);
1708 for (blob_it.mark_cycle_pt(); !blob_it.cycled_list(); blob_it.forward()) {
1709 blob = blob_it.data();
1713 height = (gradient * xcentre + row->
parallel_c() -
1715 if (height >= min_height && height <= max_height)
1716 heights.
add(static_cast<int>(floor(height + 0.5)), 1);
1719 int blob_index = heights.
mode();
1720 int blob_count = heights.
pile_count(blob_index);
1721 float total_fraction =
1723 if (static_cast<float>(blob_count + num_potential_asc) <
1724 xheight_blob_count * total_fraction) {
1727 int descdrop = blob_count > 0 ? -blob_index : 0;
1729 tprintf(
"Descdrop: %d (potential ascenders %d, descenders %d)\n",
1730 descdrop, num_potential_asc, blob_count);
1755 src_count = max_height + 1 - min_height;
1759 for (src_index = 0; src_index < src_count; src_index++) {
1760 pile_count = heights->
pile_count(min_height + src_index);
1761 if (pile_count > 0) {
1762 if (dest_count < maxmodes) {
1763 if (pile_count < least_count) {
1765 least_count = pile_count;
1766 least_index = dest_count;
1768 modes[dest_count++] = min_height + src_index;
1769 }
else if (pile_count >= least_count) {
1770 while (least_index < maxmodes - 1) {
1771 modes[least_index] = modes[least_index + 1];
1776 modes[maxmodes - 1] = min_height + src_index;
1777 if (pile_count == least_count) {
1779 least_index = maxmodes - 1;
1783 for (dest_count = 1; dest_count < maxmodes; dest_count++) {
1784 pile_count = heights->
pile_count(modes[dest_count]);
1785 if (pile_count < least_count) {
1787 least_count = pile_count;
1788 least_index = dest_count;
1806 float ascrise,
float descdrop) {
1809 tprintf(
"correcting row xheight: row->xheight %.4f"
1810 ", row->acrise %.4f row->descdrop %.4f\n",
1813 bool normal_xheight =
1830 (normal_xheight || cap_xheight)) ||
1831 (row_category ==
ROW_UNKNOWN && normal_xheight)) {
1855 if (row->
xheight < xheight + ascrise && row->
xheight > xheight) {
1858 tprintf(
"all caps with irregular xheight\n");
1867 tprintf(
"corrected row->xheight = %.4f, row->acrise = %.4f, row->descdrop"
1872 static int CountOverlaps(
const TBOX& box,
int min_height,
1873 BLOBNBOX_LIST* blobs) {
1875 BLOBNBOX_IT blob_it(blobs);
1876 for (blob_it.mark_cycle_pt(); !blob_it.cycled_list(); blob_it.forward()) {
1903 BLOBNBOX_IT blob_it;
1907 TO_ROW_IT row_it = block->
get_rows();
1912 length = sqrt(1 + gradient * gradient);
1913 g_vec =
FCOORD(1 / length, -gradient / length);
1914 blob_rotation =
FCOORD(rotation.
x(), -rotation.
y());
1915 blob_rotation.
rotate(g_vec);
1916 for (row_it.mark_cycle_pt(); !row_it.cycled_list(); row_it.forward()) {
1917 row = row_it.data();
1920 for (blob_it.mark_cycle_pt(); !blob_it.cycled_list();
1921 blob_it.forward()) {
1922 blob = blob_it.data();
1930 rotated_blob, static_cast<inT16>(row->
intercept()),
1935 under_it.add_after_then_move(blob_it.extract());
1937 tprintf(
"Underlined blob at:");
1938 rotated_blob->bounding_box().print();
1942 }
else if (CountOverlaps(blob->
bounding_box(), min_blob_height,
1945 large_it.add_after_then_move(blob_it.extract());
1947 tprintf(
"Large blob overlaps %d blobs at:",
1948 CountOverlaps(blob_box, min_blob_height,
1953 delete rotated_blob;
1971 #ifndef GRAPHICS_DISABLED
1978 BLOBNBOX_IT blob_it;
1979 BLOBNBOX_IT start_it;
1980 TO_ROW_IT row_it = block->
get_rows ();
1982 #ifndef GRAPHICS_DISABLED
1986 blob_rotation =
FCOORD (rotation.
x (), -rotation.
y ());
1987 for (row_it.mark_cycle_pt (); !row_it.cycled_list (); row_it.forward ()) {
1989 blob_it.set_to_list (row_it.data ()->blob_list ());
1990 for (blob_it.mark_cycle_pt (); !blob_it.cycled_list ();
1991 blob_it.forward ()) {
1992 blob = blob_it.data ();
2005 if (!blob_it.at_last ()) {
2006 nextblob = blob_it.data_relative(1);
2009 blob->
merge(nextblob);
2016 blob->
chop (&start_it, &blob_it,
2022 #ifndef GRAPHICS_DISABLED
2027 for (blob_it.mark_cycle_pt (); !blob_it.cycled_list ();
2028 blob_it.forward ()) {
2029 blob = blob_it.data ();
2031 blob_box.
rotate (rotation);
2034 blob_box.
right (), blob_box.
top ());
2058 #ifndef GRAPHICS_DISABLED
2061 TO_ROW_IT row_it = block->
get_rows ();
2063 row_it.move_to_first ();
2064 for (row_it.mark_cycle_pt (); !row_it.cycled_list (); row_it.forward ()) {
2065 if (row_it.data ()->blob_list ()->empty ())
2066 delete row_it.extract ();
2070 #ifndef GRAPHICS_DISABLED
2073 for (row_it.mark_cycle_pt (); !row_it.cycled_list (); row_it.forward ()) {
2075 block_edge, colour, rotation);
2100 for (blob_it.mark_cycle_pt(); !blob_it.cycled_list(); blob_it.forward()) {
2101 if (!blob_it.data()->joined_to_prev()) {
2102 const TBOX& box = blob_it.data()->bounding_box();
2110 error = lms.
Fit(&gradient, &c);
2123 void Textord::make_spline_rows(
TO_BLOCK *block,
2128 #ifndef GRAPHICS_DISABLED
2131 TO_ROW_IT row_it = block->
get_rows ();
2133 row_it.move_to_first ();
2134 for (row_it.mark_cycle_pt (); !row_it.cycled_list (); row_it.forward ()) {
2135 if (row_it.data ()->blob_list ()->empty ())
2136 delete row_it.extract ();
2141 #ifndef GRAPHICS_DISABLED
2144 for (row_it.mark_cycle_pt (); !row_it.cycled_list ();
2145 row_it.forward ()) {
2146 row_it.data ()->baseline.plot (
to_win, colour);
2153 make_old_baselines(block, testing_on, gradient);
2155 #ifndef GRAPHICS_DISABLED
2158 for (row_it.mark_cycle_pt (); !row_it.cycled_list (); row_it.forward ()) {
2159 row_it.data ()->baseline.plot (
to_win, colour);
2180 BLOBNBOX_IT blob_it = row->
blob_list ();
2191 xstarts[1] = xstarts[segments];
2193 coeffs = (
double *)
alloc_mem (3 *
sizeof (
double));
2195 coeffs[1] = row->
line_m ();
2196 coeffs[2] = row->
line_c ();
2228 BLOBNBOX_IT blob_it = row->
blob_list ();
2229 BLOBNBOX_IT new_it = blob_it;
2232 needs_curve =
FALSE;
2234 xstarts[0] = box.
left ();
2236 blobcount = row->
blob_list ()->length ();
2238 tprintf (
"Segmenting baseline of %d blobs at (%d,%d)\n",
2242 blob_it.move_to_last ();
2243 box = blob_it.data ()->bounding_box ();
2244 xstarts[1] = box.
right ();
2248 new_it.mark_cycle_pt ();
2251 middle = (new_box.
left () + new_box.
right ()) / 2.0;
2254 yshifts.
add (yshift, blobindex);
2255 if (new_it.cycled_list ()) {
2256 xstarts[1] = new_box.
right ();
2260 for (blobcount = 0; blobcount < textord_spline_medianwin / 2; blobcount++)
2265 yshift = yshifts[textord_spline_medianwin / 2];
2277 xstarts[segments++] = box.
left ();
2281 yshifts.
remove (blobindex - textord_spline_medianwin);
2283 middle = (new_box.
left () + new_box.
right ()) / 2.0;
2285 yshifts.
add (yshift, blobindex);
2289 while (!new_it.cycled_list ());
2291 xstarts[segments] = new_box.
right ();
2294 xstarts[--segments] = new_box.
right ();
2297 tprintf (
"Made %d segments on row at (%d,%d)\n",
2320 int blobs_per_segment;
2324 BLOBNBOX_IT blob_it = row->
blob_list ();
2325 BLOBNBOX_IT new_it = blob_it;
2332 xstarts[0] = box.
left ();
2334 while (!blob_it.at_first ()) {
2341 blobs_per_segment = blobcount / segments;
2342 coeffs = (
double *)
alloc_mem (segments * 3 *
sizeof (
double));
2345 (
"Linear splining baseline of %d blobs at (%d,%d), into %d segments of %d blobs\n",
2346 blobcount, box.
left (), box.
bottom (), segments, blobs_per_segment);
2348 for (index2 = 0; index2 < blobs_per_segment / 2; index2++)
2353 blobindex += blobs_per_segment;
2355 while (index1 < blobindex || (segment == segments && index1 < blobcount)) {
2357 int middle = (box.
left() + box.
right()) / 2;
2360 if (index1 == blobindex - blobs_per_segment / 2
2361 || index1 == blobcount - 1) {
2362 xstarts[segment] = box.
left ();
2366 coeffs[segment * 3 - 3] = 0;
2367 coeffs[segment * 3 - 2] = b;
2368 coeffs[segment * 3 - 1] = c;
2370 if (segment > segments)
2373 blobindex += blobs_per_segment;
2375 while (index2 < blobindex || (segment == segments && index2 < blobcount)) {
2377 int middle = (new_box.
left() + new_box.
right()) / 2;
2380 if (index2 == blobindex - blobs_per_segment / 2
2381 || index2 == blobcount - 1) {
2382 xstarts[segment] = new_box.
left ();
2386 coeffs[segment * 3 - 3] = 0;
2387 coeffs[segment * 3 - 2] = b;
2388 coeffs[segment * 3 - 1] = c;
2391 while (segment <= segments);
2406 BOOL8 reject_misses,
2407 BOOL8 make_new_rows,
2413 float g_length = 1.0f;
2418 float smooth_factor;
2425 BLOBNBOX_IT blob_it = &block->
blobs;
2426 TO_ROW_IT row_it = block->
get_rows ();
2431 if (gradient !=
NULL)
2432 g_length = sqrt (1 + *gradient * *gradient);
2433 #ifndef GRAPHICS_DISABLED
2439 smooth_factor = 1.0;
2441 row_count = row_it.length ();
2442 if (!blob_it.empty ()) {
2443 left_x = blob_it.data ()->bounding_box ().left ();
2449 for (blob_it.mark_cycle_pt (); !blob_it.cycled_list (); blob_it.forward ()) {
2450 blob = blob_it.data ();
2451 if (gradient !=
NULL) {
2456 && last_x - left_x > block->
line_size * 2
2460 / (last_x - left_x);
2466 #ifndef GRAPHICS_DISABLED
2470 if (!row_it.empty ()) {
2471 for (row_it.move_to_first ();
2472 !row_it.at_last () && row_it.data ()->min_y () > top;
2474 row = row_it.data ();
2475 if (row->
min_y () <= top && row->
max_y () >= bottom) {
2483 if (overlap_result ==
NEW_ROW && !reject_misses)
2488 if (!make_new_rows) {
2489 near_dist = row_it.data_relative (-1)->min_y () - top;
2491 if (bottom < row->min_y ()) {
2492 if (row->
min_y () - bottom <=
2500 else if (near_dist > 0
2501 && near_dist < bottom - row->max_y ()) {
2503 dest_row = row_it.data ();
2504 if (dest_row->
min_y () - bottom <=
2512 if (top - row->
max_y () <=
2523 if (overlap_result ==
ASSIGN)
2524 dest_row->
add_blob (blob_it.extract (), top, bottom,
2526 if (overlap_result ==
NEW_ROW) {
2527 if (make_new_rows && top - bottom < block->max_blob_size) {
2529 new TO_ROW (blob_it.extract (), top, bottom,
2532 if (bottom > row_it.data ()->min_y ())
2533 row_it.add_before_then_move (dest_row);
2536 row_it.add_after_then_move (dest_row);
2545 else if (make_new_rows && top - bottom < block->max_blob_size) {
2550 row_it.add_after_then_move (dest_row);
2557 if (overlap_result !=
REJECT) {
2558 tprintf (
"Test blob assigned to row at (%g,%g) on pass %d\n",
2559 dest_row->
min_y (), dest_row->
max_y (), pass);
2562 tprintf (
"Test blob assigned to no row on pass %d\n", pass);
2565 if (overlap_result !=
REJECT) {
2566 while (!row_it.at_first ()
2567 && row_it.data ()->min_y () >
2568 row_it.data_relative (-1)->min_y ()) {
2569 row = row_it.extract ();
2571 row_it.add_before_then_move (row);
2573 while (!row_it.at_last ()
2574 && row_it.data ()->min_y () <
2575 row_it.data_relative (1)->min_y ()) {
2576 row = row_it.extract ();
2579 row_it.add_after_then_move (row);
2581 block_skew = (1 - smooth_factor) * block_skew
2586 for (row_it.mark_cycle_pt (); !row_it.cycled_list (); row_it.forward ()) {
2587 if (row_it.data ()->blob_list ()->empty ())
2588 delete row_it.extract ();
2609 float merge_top, merge_bottom;
2613 BLOBNBOX_IT blob_it;
2616 row = row_it->data ();
2617 bestover = top - bottom;
2618 if (top > row->
max_y ())
2619 bestover -= top - row->
max_y ();
2620 if (bottom < row->min_y ())
2622 bestover -= row->
min_y () - bottom;
2624 tprintf (
"Test blob y=(%g,%g), row=(%f,%f), overlap=%f\n",
2625 bottom, top, row->
min_y (), row->
max_y (), bestover);
2629 if (!row_it->at_last ()) {
2631 test_row = row_it->data ();
2632 if (test_row->
min_y () <= top && test_row->
max_y () >= bottom) {
2634 test_row->
max_y () >
2637 test_row->
min_y () <
2639 if (merge_top - merge_bottom <= rowsize) {
2641 tprintf (
"Merging rows at (%g,%g), (%g,%g)\n",
2645 test_row->
set_limits (merge_bottom, merge_top);
2646 blob_it.set_to_list (test_row->
blob_list ());
2647 blob_it.add_list_after (row->
blob_list ());
2649 row_it->backward ();
2650 delete row_it->extract ();
2654 overlap = top - bottom;
2655 if (top > test_row->
max_y ())
2656 overlap -= top - test_row->
max_y ();
2657 if (bottom < test_row->min_y ())
2658 overlap -= test_row->
min_y () - bottom;
2659 if (bestover >= rowsize - 1 && overlap >= rowsize - 1) {
2662 if (overlap > bestover) {
2668 (
"Test blob y=(%g,%g), row=(%f,%f), overlap=%f->%f\n",
2669 bottom, top, test_row->
min_y (), test_row->
max_y (),
2675 while (!row_it->at_last ()
2676 && test_row->
min_y () <= top && test_row->
max_y () >= bottom);
2677 while (row_it->data () != row)
2678 row_it->backward ();
2696 const void *item2) {
2718 const void *item2) {
2740 const void *item2) {
2762 int num_repeated_sets = 0;
2763 if (!box_it.empty()) {
2766 int repeat_length = 0;
2769 BLOBNBOX_IT test_it(box_it);
2770 for (test_it.forward(); !test_it.at_first(); test_it.forward()) {
2771 bblob = test_it.data();
2775 tprintf(
"Cancelled repeat of length %d due to %s\n",
2785 num_repeated_sets++;
2786 for (; repeat_length > 0; box_it.forward(), --repeat_length) {
2787 bblob = box_it.data();
2790 if (!box_it.at_first())
2796 }
while (!box_it.at_first());