31#include <allheaders.h>
37static INT_VAR(textord_tabfind_show_images,
false,
"Show image blobs");
61static bool HScanForEdge(uint32_t *data,
int wpl,
int x_start,
int x_end,
int min_count,
62 int mid_width,
int max_count,
int y_end,
int y_step,
int *y_start) {
64 for (
int y = *y_start; y != y_end; y += y_step) {
68 uint32_t *line = data + wpl * y;
69 for (
int x = x_start; x < x_end; ++x) {
70 if (GET_DATA_BIT(line, x)) {
74 if (mid_rows == 0 && pix_count < min_count) {
80 if (pix_count > max_count) {
84 if (mid_rows > mid_width) {
98static bool VScanForEdge(uint32_t *data,
int wpl,
int y_start,
int y_end,
int min_count,
99 int mid_width,
int max_count,
int x_end,
int x_step,
int *x_start) {
101 for (
int x = *x_start; x != x_end; x += x_step) {
103 uint32_t *line = data + y_start * wpl;
104 for (
int y = y_start; y < y_end; ++y, line += wpl) {
105 if (GET_DATA_BIT(line, x)) {
109 if (mid_cols == 0 && pix_count < min_count) {
115 if (pix_count > max_count) {
119 if (mid_cols > mid_width) {
135static bool pixNearlyRectangular(Image pix,
double min_fraction,
double max_fraction,
136 double max_skew_gradient,
int *x_start,
int *y_start,
137 int *x_end,
int *y_end) {
140 *x_end = pixGetWidth(pix);
142 *y_end = pixGetHeight(pix);
144 uint32_t *data = pixGetData(pix);
145 int wpl = pixGetWpl(pix);
146 bool any_cut =
false;
147 bool left_done =
false;
148 bool right_done =
false;
149 bool top_done =
false;
150 bool bottom_done =
false;
154 int width = *x_end - *x_start;
155 int min_count =
static_cast<int>(width * min_fraction);
156 int max_count =
static_cast<int>(width * max_fraction);
157 int edge_width =
static_cast<int>(width * max_skew_gradient);
158 if (HScanForEdge(data, wpl, *x_start, *x_end, min_count, edge_width, max_count, *y_end, 1,
165 if (HScanForEdge(data, wpl, *x_start, *x_end, min_count, edge_width, max_count, *y_start, -1,
174 int height = *y_end - *y_start;
175 min_count =
static_cast<int>(height * min_fraction);
176 max_count =
static_cast<int>(height * max_fraction);
177 edge_width =
static_cast<int>(height * max_skew_gradient);
178 if (VScanForEdge(data, wpl, *y_start, *y_end, min_count, edge_width, max_count, *x_end, 1,
185 if (VScanForEdge(data, wpl, *y_start, *y_end, min_count, edge_width, max_count, *x_start, -1,
196 return left_done && right_done && top_done && bottom_done;
206static void ConnCompAndRectangularize(Image pix, DebugPixa *pixa_debug, Boxa **boxa,
211 if (textord_tabfind_show_images && pixa_debug !=
nullptr) {
212 pixa_debug->AddPix(pix,
"Conncompimage");
215 *boxa = pixConnComp(pix, pixa, 8);
220 if (*boxa !=
nullptr && *pixa !=
nullptr) {
221 npixes = pixaGetCount(*pixa);
223 for (
int i = 0; i < npixes; ++i) {
224 int x_start, x_end, y_start, y_end;
225 Image img_pix = pixaGetPix(*pixa, i, L_CLONE);
226 if (textord_tabfind_show_images && pixa_debug !=
nullptr) {
227 pixa_debug->AddPix(img_pix,
"A component");
231 Image simple_pix = pixCreate(x_end - x_start, y_end - y_start, 1);
232 pixSetAll(simple_pix);
235 pixaReplacePix(*pixa, i, simple_pix,
nullptr);
236 img_pix = pixaGetPix(*pixa, i, L_CLONE);
238 l_int32 x, y, width, height;
239 boxaGetBoxGeometry(*boxa, i, &x, &y, &width, &height);
240 Box *simple_box = boxCreate(x + x_start, y + y_start, x_end - x_start, y_end - y_start);
241 boxaReplaceBox(*boxa, i, simple_box);
255 return pixCreate(pixGetWidth(pix), pixGetHeight(pix), 1);
259 Image pixr = pixReduceRankBinaryCascade(pix, 1, 0, 0, 0);
260 if (textord_tabfind_show_images && pixa_debug !=
nullptr) {
261 pixa_debug->
AddPix(pixr,
"CascadeReduced");
271 return pixCreate(pixGetWidth(pix), pixGetHeight(pix), 1);
274 l_int32 ht_found = 0;
275 Pixa *pixadb = (textord_tabfind_show_images && pixa_debug !=
nullptr) ? pixaCreate(0) :
nullptr;
276 Image pixht2 = pixGenerateHalftoneMask(pixr,
nullptr, &ht_found, pixadb);
278 Image pixdb = pixaDisplayTiledInColumns(pixadb, 3, 1.0, 20, 2);
279 if (textord_tabfind_show_images && pixa_debug !=
nullptr) {
280 pixa_debug->
AddPix(pixdb,
"HalftoneMask");
283 pixaDestroy(&pixadb);
286 if (!ht_found && pixht2 !=
nullptr) {
289 if (pixht2 ==
nullptr) {
290 return pixCreate(pixGetWidth(pix), pixGetHeight(pix), 1);
294 Image pixht = pixExpandReplicate(pixht2, 2);
295 if (textord_tabfind_show_images && pixa_debug !=
nullptr) {
296 pixa_debug->
AddPix(pixht,
"HalftoneReplicated");
301 Image pixt = pixSeedfillBinary(
nullptr, pixht, pix, 8);
306 Image pixfinemask = pixReduceRankBinaryCascade(pixht, 1, 1, 3, 3);
307 pixDilateBrick(pixfinemask, pixfinemask, 5, 5);
308 if (textord_tabfind_show_images && pixa_debug !=
nullptr) {
309 pixa_debug->
AddPix(pixfinemask,
"FineMask");
311 Image pixreduced = pixReduceRankBinaryCascade(pixht, 1, 1, 1, 1);
312 Image pixreduced2 = pixReduceRankBinaryCascade(pixreduced, 3, 3, 3, 0);
314 pixDilateBrick(pixreduced2, pixreduced2, 5, 5);
315 Image pixcoarsemask = pixExpandReplicate(pixreduced2, 8);
317 if (textord_tabfind_show_images && pixa_debug !=
nullptr) {
318 pixa_debug->
AddPix(pixcoarsemask,
"CoarseMask");
321 pixcoarsemask &= pixfinemask;
324 pixDilateBrick(pixcoarsemask, pixcoarsemask, 3, 3);
325 Image pixmask = pixExpandReplicate(pixcoarsemask, 16);
327 if (textord_tabfind_show_images && pixa_debug !=
nullptr) {
328 pixa_debug->
AddPix(pixmask,
"MaskDilated");
333 if (textord_tabfind_show_images && pixa_debug !=
nullptr) {
334 pixa_debug->
AddPix(pixht,
"FinalMask");
337 Image result = pixCreate(pixGetWidth(pix), pixGetHeight(pix), 1);
348 Box *input_box = boxCreate(*x_start, *y_start, *x_end - *x_start, *y_end - *y_start);
349 Box *output_box =
nullptr;
350 pixClipBoxToForeground(pix, input_box,
nullptr, &output_box);
351 bool result = output_box !=
nullptr;
353 l_int32 x, y, width, height;
354 boxGetGeometry(output_box, &x, &y, &width, &height);
359 boxDestroy(&output_box);
361 boxDestroy(&input_box);
369 const uint8_t *point) {
373 line_vector[i] =
static_cast<int>(line2[i]) -
static_cast<int>(line1[i]);
374 point_vector[i] =
static_cast<int>(point[i]) -
static_cast<int>(line1[i]);
376 line_vector[L_ALPHA_CHANNEL] = 0;
379 cross[COLOR_RED] = line_vector[COLOR_GREEN] * point_vector[COLOR_BLUE] -
380 line_vector[COLOR_BLUE] * point_vector[COLOR_GREEN];
381 cross[COLOR_GREEN] = line_vector[COLOR_BLUE] * point_vector[COLOR_RED] -
382 line_vector[COLOR_RED] * point_vector[COLOR_BLUE];
383 cross[COLOR_BLUE] = line_vector[COLOR_RED] * point_vector[COLOR_GREEN] -
384 line_vector[COLOR_GREEN] * point_vector[COLOR_RED];
385 cross[L_ALPHA_CHANNEL] = 0;
387 double cross_sq = 0.0;
388 double line_sq = 0.0;
390 cross_sq +=
static_cast<double>(cross[j]) * cross[j];
391 line_sq +=
static_cast<double>(line_vector[j]) * line_vector[j];
393 if (line_sq == 0.0) {
396 return cross_sq / line_sq;
443 TBOX search_box(box1);
446 if (box1.
x_gap(box2) <= 0) {
452 if (box1.
y_gap(box2) <= 0) {
471 TBOX rotated_im_box(im_box);
472 rotated_im_box.
rotate(rotation);
474 pixRasterop(rect_pix, 0, 0, box.
width(), box.
height(), PIX_SRC, pix,
475 box.
left() - rotated_im_box.
left(), rotated_im_box.
top() - box.
top());
477 pixCountPixels(rect_pix, &result,
nullptr);
486static void AttemptToShrinkBox(
const FCOORD &rotation,
const FCOORD &rerotation,
const TBOX &im_box,
488 TBOX rotated_box(*slice);
489 rotated_box.rotate(rerotation);
490 TBOX rotated_im_box(im_box);
491 rotated_im_box.rotate(rerotation);
492 int left = rotated_box.left() - rotated_im_box.left();
493 int right = rotated_box.right() - rotated_im_box.left();
494 int top = rotated_im_box.top() - rotated_box.top();
495 int bottom = rotated_im_box.top() - rotated_box.bottom();
497 top = rotated_im_box.top() - top;
498 bottom = rotated_im_box.top() - bottom;
499 left += rotated_im_box.left();
500 right += rotated_im_box.left();
501 rotated_box.set_to_given_coords(left, bottom, right, top);
502 rotated_box.rotate(rotation);
503 slice->
set_left(rotated_box.left());
531static void CutChunkFromParts(
const TBOX &box,
const TBOX &im_box,
const FCOORD &rotation,
532 const FCOORD &rerotation, Image pix, ColPartition_LIST *part_list) {
534 ColPartition_IT part_it(part_list);
536 ColPartition *part = part_it.data();
537 TBOX part_box = part->bounding_box();
538 if (part_box.overlap(box)) {
544 if (box.top() < part_box.top()) {
545 TBOX slice(part_box);
548 AttemptToShrinkBox(rotation, rerotation, im_box, pix, &slice);
549 part_it.add_before_stay_put(
554 if (box.left() > part_box.left()) {
555 TBOX slice(part_box);
557 if (box.top() < part_box.top()) {
560 if (box.bottom() > part_box.bottom()) {
564 AttemptToShrinkBox(rotation, rerotation, im_box, pix, &slice);
565 part_it.add_before_stay_put(
570 if (box.right() < part_box.right()) {
571 TBOX slice(part_box);
573 if (box.top() < part_box.top()) {
576 if (box.bottom() > part_box.bottom()) {
580 AttemptToShrinkBox(rotation, rerotation, im_box, pix, &slice);
581 part_it.add_before_stay_put(
586 if (box.bottom() > part_box.bottom()) {
587 TBOX slice(part_box);
590 AttemptToShrinkBox(rotation, rerotation, im_box, pix, &slice);
591 part_it.add_before_stay_put(
596 delete part_it.extract();
599 }
while (!part_it.at_first());
609static void DivideImageIntoParts(
const TBOX &im_box,
const FCOORD &rotation,
610 const FCOORD &rerotation, Image pix,
613 ColPartition *pix_part =
615 ColPartition_IT part_it(part_list);
616 part_it.add_after_then_move(pix_part);
618 rectsearch->StartRectSearch(im_box);
620 while ((part = rectsearch->NextRectSearch()) !=
nullptr) {
621 TBOX part_box = part->bounding_box();
622 if (part_box.contains(im_box) && part->flow() >=
BTFT_CHAIN) {
624 for (part_it.move_to_first(); !part_it.empty(); part_it.forward()) {
625 ColPartition *pix_part = part_it.extract();
626 pix_part->DeleteBoxes();
631 TBOX overlap_box = part_box.intersection(im_box);
634 if (black_area * 2 < part_box.area() || !im_box.contains(part_box)) {
637 int padding = part->blob_type() ==
BRT_VERT_TEXT ? part_box.width() : part_box.height();
638 part_box.set_top(part_box.top() + padding / 2);
639 part_box.set_bottom(part_box.bottom() - padding / 2);
640 CutChunkFromParts(part_box, im_box, rotation, rerotation, pix, part_list);
646 if (part_list->empty()) {
654static int ExpandImageLeft(
const TBOX &box,
int left_limit, ColPartitionGrid *part_grid) {
658 search.StartSideSearch(box.left(), box.bottom(), box.top());
659 while ((part =
search.NextSideSearch(
true)) !=
nullptr) {
661 const TBOX &part_box(part->bounding_box());
662 if (part_box.y_gap(box) < 0) {
663 if (part_box.right() > left_limit && part_box.right() < box.left()) {
664 left_limit = part_box.right();
670 if (part !=
nullptr) {
672 TBOX search_box(left_limit, box.bottom(), box.left(), box.top());
673 search.StartRectSearch(search_box);
674 while ((part =
search.NextRectSearch()) !=
nullptr) {
676 const TBOX &part_box(part->bounding_box());
677 if (part_box.y_gap(box) < 0) {
678 if (part_box.right() > left_limit && part_box.right() < box.left()) {
679 left_limit = part_box.right();
690static int ExpandImageRight(
const TBOX &box,
int right_limit, ColPartitionGrid *part_grid) {
694 search.StartSideSearch(box.right(), box.bottom(), box.top());
695 while ((part =
search.NextSideSearch(
false)) !=
nullptr) {
697 const TBOX &part_box(part->bounding_box());
698 if (part_box.y_gap(box) < 0) {
699 if (part_box.left() < right_limit && part_box.left() > box.right()) {
700 right_limit = part_box.left();
706 if (part !=
nullptr) {
708 TBOX search_box(box.left(), box.bottom(), right_limit, box.top());
709 search.StartRectSearch(search_box);
710 while ((part =
search.NextRectSearch()) !=
nullptr) {
712 const TBOX &part_box(part->bounding_box());
713 if (part_box.y_gap(box) < 0) {
714 if (part_box.left() < right_limit && part_box.left() > box.right()) {
715 right_limit = part_box.left();
726static int ExpandImageBottom(
const TBOX &box,
int bottom_limit, ColPartitionGrid *part_grid) {
730 search.StartVerticalSearch(box.left(), box.right(), box.bottom());
731 while ((part =
search.NextVerticalSearch(
true)) !=
nullptr) {
733 const TBOX &part_box(part->bounding_box());
734 if (part_box.x_gap(box) < 0) {
735 if (part_box.top() > bottom_limit && part_box.top() < box.bottom()) {
736 bottom_limit = part_box.top();
742 if (part !=
nullptr) {
744 TBOX search_box(box.left(), bottom_limit, box.right(), box.bottom());
745 search.StartRectSearch(search_box);
746 while ((part =
search.NextRectSearch()) !=
nullptr) {
748 const TBOX &part_box(part->bounding_box());
749 if (part_box.x_gap(box) < 0) {
750 if (part_box.top() > bottom_limit && part_box.top() < box.bottom()) {
751 bottom_limit = part_box.top();
762static int ExpandImageTop(
const TBOX &box,
int top_limit, ColPartitionGrid *part_grid) {
766 search.StartVerticalSearch(box.left(), box.right(), box.top());
767 while ((part =
search.NextVerticalSearch(
false)) !=
nullptr) {
769 const TBOX &part_box(part->bounding_box());
770 if (part_box.x_gap(box) < 0) {
771 if (part_box.bottom() < top_limit && part_box.bottom() > box.top()) {
772 top_limit = part_box.bottom();
778 if (part !=
nullptr) {
780 TBOX search_box(box.left(), box.top(), box.right(), top_limit);
781 search.StartRectSearch(search_box);
782 while ((part =
search.NextRectSearch()) !=
nullptr) {
784 const TBOX &part_box(part->bounding_box());
785 if (part_box.x_gap(box) < 0) {
786 if (part_box.bottom() < top_limit && part_box.bottom() > box.top()) {
787 top_limit = part_box.bottom();
800static int ExpandImageDir(
BlobNeighbourDir dir,
const TBOX &im_box,
const TBOX &limit_box,
801 ColPartitionGrid *part_grid, TBOX *expanded_box) {
802 *expanded_box = im_box;
805 expanded_box->set_left(ExpandImageLeft(im_box, limit_box.left(), part_grid));
808 expanded_box->set_right(ExpandImageRight(im_box, limit_box.right(), part_grid));
811 expanded_box->set_top(ExpandImageTop(im_box, limit_box.top(), part_grid));
814 expanded_box->set_bottom(ExpandImageBottom(im_box, limit_box.bottom(), part_grid));
819 return expanded_box->area() - im_box.area();
826static void MaximalImageBoundingBox(ColPartitionGrid *part_grid, TBOX *im_box) {
828 memset(dunnit, 0,
sizeof(dunnit));
829 TBOX limit_box(part_grid->bleft().x(), part_grid->bleft().y(), part_grid->tright().x(),
830 part_grid->tright().y());
831 TBOX text_box(*im_box);
832 for (
int iteration = 0; iteration <
BND_COUNT; ++iteration) {
837 for (
int dir = 0; dir <
BND_COUNT; ++dir) {
841 int area_delta = ExpandImageDir(bnd, text_box, limit_box, part_grid, &expanded_boxes[bnd]);
842 if (best_delta < 0 || area_delta < best_delta) {
843 best_delta = area_delta;
849 dunnit[best_dir] =
true;
850 text_box = expanded_boxes[best_dir];
859static void DeletePartition(ColPartition *part) {
868 part->SetBlobTypes();
887 ColPartitionGrid *part_grid, ColPartition **part_ptr) {
888 ColPartition *image_part = *part_ptr;
889 TBOX im_part_box = image_part->bounding_box();
890 if (textord_tabfind_show_images > 1) {
891 tprintf(
"Searching for merge with image part:");
894 max_image_box.print();
896 rectsearch->StartRectSearch(max_image_box);
898 ColPartition *best_part =
nullptr;
900 while ((part = rectsearch->NextRectSearch()) !=
nullptr) {
901 if (textord_tabfind_show_images > 1) {
902 tprintf(
"Considering merge with part:");
904 if (im_part_box.contains(part->bounding_box())) {
906 }
else if (!max_image_box.contains(part->bounding_box())) {
907 tprintf(
"Not within text box\n");
918 TBOX box = part->bounding_box();
919 if (max_image_box.contains(box) && part->blob_type() !=
BRT_NOISE) {
920 if (im_part_box.contains(box)) {
922 rectsearch->RemoveBBox();
923 DeletePartition(part);
926 int x_dist = std::max(0, box.x_gap(im_part_box));
927 int y_dist = std::max(0, box.y_gap(im_part_box));
928 int dist = x_dist * x_dist + y_dist * y_dist;
929 if (dist > box.area() || dist > im_part_box.area()) {
932 if (best_part ==
nullptr || dist < best_dist) {
939 if (best_part !=
nullptr) {
941 TBOX box = best_part->bounding_box();
942 if (textord_tabfind_show_images > 1) {
943 tprintf(
"Merging image part:");
950 DeletePartition(image_part);
951 part_grid->RemoveBBox(best_part);
952 DeletePartition(best_part);
953 rectsearch->RepositionIterator();
961static int IntersectArea(
const TBOX &box, ColPartition_LIST *part_list) {
962 int intersect_area = 0;
963 ColPartition_IT part_it(part_list);
965 for (part_it.mark_cycle_pt(); !part_it.cycled_list(); part_it.forward()) {
966 ColPartition *image_part = part_it.data();
967 TBOX intersect = box.intersection(image_part->bounding_box());
968 intersect_area += intersect.area();
970 return intersect_area;
978static bool TestWeakIntersectedPart(
const TBOX &im_box, ColPartition_LIST *part_list,
979 ColPartition *part) {
982 const TBOX &part_box = part->bounding_box();
983 if (im_box.contains(part_box)) {
984 int area = part_box.area();
985 int intersect_area = IntersectArea(part_box, part_list);
986 if (area < 2 * intersect_area) {
999static void EliminateWeakParts(
const TBOX &im_box, ColPartitionGrid *part_grid,
1000 ColPartition_LIST *big_parts, ColPartition_LIST *part_list) {
1003 rectsearch.StartRectSearch(im_box);
1004 while ((part = rectsearch.NextRectSearch()) !=
nullptr) {
1005 if (TestWeakIntersectedPart(im_box, part_list, part)) {
1008 rectsearch.RemoveBBox();
1009 DeletePartition(part);
1015 part->SetBlobTypes();
1019 ColPartition_IT big_it(big_parts);
1020 for (big_it.mark_cycle_pt(); !big_it.cycled_list(); big_it.forward()) {
1021 part = big_it.data();
1022 if (TestWeakIntersectedPart(im_box, part_list, part)) {
1024 DeletePartition(big_it.extract());
1033static bool ScanForOverlappingText(ColPartitionGrid *part_grid, TBOX *box) {
1035 TBOX padded_box(*box);
1037 rectsearch.StartRectSearch(padded_box);
1039 bool any_text_in_padded_rect =
false;
1040 while ((part = rectsearch.NextRectSearch()) !=
nullptr) {
1043 any_text_in_padded_rect =
true;
1044 const TBOX &part_box = part->bounding_box();
1045 if (box->overlap(part_box)) {
1050 if (!any_text_in_padded_rect) {
1060static void MarkAndDeleteImageParts(
const FCOORD &rerotate, ColPartitionGrid *part_grid,
1061 ColPartition_LIST *image_parts, Image image_pix) {
1062 if (image_pix ==
nullptr) {
1065 int imageheight = pixGetHeight(image_pix);
1066 ColPartition_IT part_it(image_parts);
1067 for (; !part_it.empty(); part_it.forward()) {
1068 ColPartition *part = part_it.extract();
1069 TBOX part_box = part->bounding_box();
1071 if (!ScanForOverlappingText(part_grid, &part_box) || type ==
BRT_RECTIMAGE ||
1075 part_box.rotate(rerotate);
1076 int left = part_box.left();
1077 int top = part_box.top();
1078 pixRasterop(image_pix, left, imageheight - top, part_box.width(), part_box.height(), PIX_SET,
1081 DeletePartition(part);
1095 ColPartition_LIST parts_list;
1096 ColPartition_IT part_it(&parts_list);
1103 part_it.add_after_then_move(part);
1108 MarkAndDeleteImageParts(rerotation, part_grid, &parts_list, image_mask);
1115 if (part_grid !=
nullptr) {
1119 gsearch.StartFullSearch();
1121 while ((part = gsearch.NextFullSearch()) !=
nullptr) {
1125 const TBOX &part_box = part->bounding_box();
1128 gsearch.RemoveBBox();
1129 DeletePartition(part);
1148 ColPartition_LIST *big_parts) {
1149 int imageheight = pixGetHeight(image_pix);
1152 ConnCompAndRectangularize(image_pix, pixa_debug, &boxa, &pixa);
1155 if (boxa !=
nullptr && pixa !=
nullptr) {
1156 nboxes = boxaGetCount(boxa);
1158 for (
int i = 0; i < nboxes; ++i) {
1159 l_int32 x, y, width, height;
1160 boxaGetBoxGeometry(boxa, i, &x, &y, &width, &height);
1161 Image pix = pixaGetPix(pixa, i, L_CLONE);
1162 TBOX im_box(x, imageheight - y - height, x + width, imageheight - y);
1166 ColPartition_LIST part_list;
1167 DivideImageIntoParts(im_box, rotation, rerotation, pix, &rectsearch, &part_list);
1168 if (textord_tabfind_show_images && pixa_debug !=
nullptr) {
1169 pixa_debug->
AddPix(pix,
"ImageComponent");
1170 tprintf(
"Component has %d parts\n", part_list.length());
1173 if (!part_list.empty()) {
1174 ColPartition_IT part_it(&part_list);
1175 if (part_list.singleton()) {
1180 TBOX text_box(im_box);
1181 MaximalImageBoundingBox(part_grid, &text_box);
1182 while (ExpandImageIntoParts(text_box, &rectsearch, part_grid, &part)) {
1185 part_it.set_to_list(&part_list);
1186 part_it.add_after_then_move(part);
1189 EliminateWeakParts(im_box, part_grid, big_parts, &part_list);
1191 for (part_it.move_to_first(); !part_it.empty(); part_it.forward()) {
1194 part_grid->
InsertBBox(
true,
true, image_part);
1195 if (!part_it.at_last()) {
1205 DeleteSmallImages(part_grid);
1206#ifndef GRAPHICS_DISABLED
1207 if (textord_tabfind_show_images) {
#define INT_VAR(name, val, comment)
const double kMinRectangularFraction
void tprintf(const char *format,...)
GridSearch< ColPartition, ColPartition_CLIST, ColPartition_C_IT > ColPartitionGridSearch
LIST search(LIST list, void *key, int_compare is_equal)
const double kMaxRectangularGradient
const int kMinImageFindSize
const double kMaxRectangularFraction
void AddPix(const Image pix, const char *caption)
int y_gap(const TBOX &box) const
TDimension height() const
void rotate(const FCOORD &vec)
int x_gap(const TBOX &box) const
TDimension bottom() const
void SetUniqueMode(bool mode)
void DisplayBoxes(ScrollView *window)
void InsertBBox(bool h_spread, bool v_spread, BBC *bbox)
ScrollView * MakeWindow(int x, int y, const char *window_name)
static ColPartition * FakePartition(const TBOX &box, PolyBlockType block_type, BlobRegionType blob_type, BlobTextFlowType flow)
BlobRegionType blob_type() const
const TBOX & bounding_box() const
void AddPartner(bool upper, ColPartition *partner)
static bool BlankImageInBetween(const TBOX &box1, const TBOX &box2, const TBOX &im_box, const FCOORD &rotation, Image pix)
static bool BoundsWithinRect(Image pix, int *x_start, int *y_start, int *x_end, int *y_end)
static void FindImagePartitions(Image image_pix, const FCOORD &rotation, const FCOORD &rerotation, TO_BLOCK *block, TabFind *tab_grid, DebugPixa *pixa_debug, ColPartitionGrid *part_grid, ColPartition_LIST *big_parts)
static void TransferImagePartsToImageMask(const FCOORD &rerotation, ColPartitionGrid *part_grid, Image image_mask)
static Image FindImages(Image pix, DebugPixa *pixa_debug)
static int CountPixelsInRotatedBox(TBOX box, const TBOX &im_box, const FCOORD &rotation, Image pix)
static double ColorDistanceFromLine(const uint8_t *line1, const uint8_t *line2, const uint8_t *point)