22 #include "config_auto.h" 31 #include "allheaders.h" 67 return pixCreate(pixGetWidth(pix), pixGetHeight(pix), 1);
70 Pix *pixr = pixReduceRankBinaryCascade(pix, 1, 0, 0, 0);
72 pixa_debug->
AddPix(pixr,
"CascadeReduced");
82 return pixCreate(pixGetWidth(pix), pixGetHeight(pix), 1);
89 Pix* pixht2 = pixGenerateHalftoneMask(pixr,
nullptr, &ht_found, pixadb);
91 Pix* pixdb = pixaDisplayTiledInColumns(pixadb, 3, 1.0, 20, 2);
93 pixa_debug->
AddPix(pixdb,
"HalftoneMask");
98 if (!ht_found && pixht2 !=
nullptr)
100 if (pixht2 ==
nullptr)
101 return pixCreate(pixGetWidth(pix), pixGetHeight(pix), 1);
104 Pix *pixht = pixExpandReplicate(pixht2, 2);
106 pixa_debug->
AddPix(pixht,
"HalftoneReplicated");
110 Pix *pixt = pixSeedfillBinary(
nullptr, pixht, pix, 8);
111 pixOr(pixht, pixht, pixt);
115 Pix* pixfinemask = pixReduceRankBinaryCascade(pixht, 1, 1, 3, 3);
116 pixDilateBrick(pixfinemask, pixfinemask, 5, 5);
118 pixa_debug->
AddPix(pixfinemask,
"FineMask");
119 Pix* pixreduced = pixReduceRankBinaryCascade(pixht, 1, 1, 1, 1);
120 Pix* pixreduced2 = pixReduceRankBinaryCascade(pixreduced, 3, 3, 3, 0);
121 pixDestroy(&pixreduced);
122 pixDilateBrick(pixreduced2, pixreduced2, 5, 5);
123 Pix* pixcoarsemask = pixExpandReplicate(pixreduced2, 8);
124 pixDestroy(&pixreduced2);
126 pixa_debug->
AddPix(pixcoarsemask,
"CoarseMask");
128 pixAnd(pixcoarsemask, pixcoarsemask, pixfinemask);
129 pixDestroy(&pixfinemask);
131 pixDilateBrick(pixcoarsemask, pixcoarsemask, 3, 3);
132 Pix* pixmask = pixExpandReplicate(pixcoarsemask, 16);
133 pixDestroy(&pixcoarsemask);
135 pixa_debug->
AddPix(pixmask,
"MaskDilated");
137 pixAnd(pixht, pixht, pixmask);
138 pixDestroy(&pixmask);
140 pixa_debug->
AddPix(pixht,
"FinalMask");
142 Pix* result = pixCreate(pixGetWidth(pix), pixGetHeight(pix), 1);
143 pixOr(result, result, pixht);
156 Boxa** boxa, Pixa** pixa) {
161 pixa_debug->
AddPix(pix,
"Conncompimage");
163 *boxa = pixConnComp(pix, pixa, 8);
168 if (*boxa !=
nullptr && *pixa !=
nullptr) npixes = pixaGetCount(*pixa);
169 for (
int i = 0; i < npixes; ++i) {
170 int x_start, x_end, y_start, y_end;
171 Pix* img_pix = pixaGetPix(*pixa, i, L_CLONE);
173 pixa_debug->
AddPix(img_pix,
"A component");
177 &x_start, &y_start, &x_end, &y_end)) {
178 Pix* simple_pix = pixCreate(x_end - x_start, y_end - y_start, 1);
179 pixSetAll(simple_pix);
180 pixDestroy(&img_pix);
182 pixaReplacePix(*pixa, i, simple_pix,
nullptr);
183 img_pix = pixaGetPix(*pixa, i, L_CLONE);
185 l_int32 x, y, width, height;
186 boxaGetBoxGeometry(*boxa, i, &x, &y, &width, &height);
187 Box* simple_box = boxCreate(x + x_start, y + y_start,
188 x_end - x_start, y_end - y_start);
189 boxaReplaceBox(*boxa, i, simple_box);
191 pixDestroy(&img_pix);
202 static bool HScanForEdge(uint32_t* data,
int wpl,
int x_start,
int x_end,
203 int min_count,
int mid_width,
int max_count,
204 int y_end,
int y_step,
int* y_start) {
206 for (
int y = *y_start; y != y_end; y += y_step) {
209 uint32_t* line = data + wpl * y;
210 for (
int x = x_start; x < x_end; ++x) {
211 if (GET_DATA_BIT(line, x))
214 if (mid_rows == 0 && pix_count < min_count)
218 if (pix_count > max_count)
221 if (mid_rows > mid_width)
234 static bool VScanForEdge(uint32_t* data,
int wpl,
int y_start,
int y_end,
235 int min_count,
int mid_width,
int max_count,
236 int x_end,
int x_step,
int* x_start) {
238 for (
int x = *x_start; x != x_end; x += x_step) {
240 uint32_t* line = data + y_start * wpl;
241 for (
int y = y_start; y < y_end; ++y, line += wpl) {
242 if (GET_DATA_BIT(line, x))
245 if (mid_cols == 0 && pix_count < min_count)
249 if (pix_count > max_count)
252 if (mid_cols > mid_width)
268 double min_fraction,
double max_fraction,
269 double max_skew_gradient,
270 int* x_start,
int* y_start,
271 int* x_end,
int* y_end) {
274 *x_end = pixGetWidth(pix);
276 *y_end = pixGetHeight(pix);
278 uint32_t* data = pixGetData(pix);
279 int wpl = pixGetWpl(pix);
280 bool any_cut =
false;
281 bool left_done =
false;
282 bool right_done =
false;
283 bool top_done =
false;
284 bool bottom_done =
false;
288 int width = *x_end - *x_start;
289 int min_count =
static_cast<int>(width * min_fraction);
290 int max_count =
static_cast<int>(width * max_fraction);
291 int edge_width =
static_cast<int>(width * max_skew_gradient);
292 if (HScanForEdge(data, wpl, *x_start, *x_end, min_count, edge_width,
293 max_count, *y_end, 1, y_start) && !top_done) {
298 if (HScanForEdge(data, wpl, *x_start, *x_end, min_count, edge_width,
299 max_count, *y_start, -1, y_end) && !bottom_done) {
306 int height = *y_end - *y_start;
307 min_count =
static_cast<int>(height * min_fraction);
308 max_count =
static_cast<int>(height * max_fraction);
309 edge_width =
static_cast<int>(height * max_skew_gradient);
310 if (VScanForEdge(data, wpl, *y_start, *y_end, min_count, edge_width,
311 max_count, *x_end, 1, x_start) && !left_done) {
316 if (VScanForEdge(data, wpl, *y_start, *y_end, min_count, edge_width,
317 max_count, *x_start, -1, x_end) && !right_done) {
326 return left_done && right_done && top_done && bottom_done;
334 int* x_end,
int* y_end) {
335 Box* input_box = boxCreate(*x_start, *y_start, *x_end - *x_start,
337 Box* output_box =
nullptr;
338 pixClipBoxToForeground(pix, input_box,
nullptr, &output_box);
339 bool result = output_box !=
nullptr;
341 l_int32 x, y, width, height;
342 boxGetGeometry(output_box, &x, &y, &width, &height);
347 boxDestroy(&output_box);
349 boxDestroy(&input_box);
357 const uint8_t* line2,
358 const uint8_t* point) {
362 line_vector[i] =
static_cast<int>(line2[i]) - static_cast<int>(line1[i]);
363 point_vector[i] =
static_cast<int>(point[i]) - static_cast<int>(line1[i]);
365 line_vector[L_ALPHA_CHANNEL] = 0;
368 cross[COLOR_RED] = line_vector[COLOR_GREEN] * point_vector[COLOR_BLUE]
369 - line_vector[COLOR_BLUE] * point_vector[COLOR_GREEN];
370 cross[COLOR_GREEN] = line_vector[COLOR_BLUE] * point_vector[COLOR_RED]
371 - line_vector[COLOR_RED] * point_vector[COLOR_BLUE];
372 cross[COLOR_BLUE] = line_vector[COLOR_RED] * point_vector[COLOR_GREEN]
373 - line_vector[COLOR_GREEN] * point_vector[COLOR_RED];
374 cross[L_ALPHA_CHANNEL] = 0;
376 double cross_sq = 0.0;
377 double line_sq = 0.0;
379 cross_sq +=
static_cast<double>(cross[j]) * cross[j];
380 line_sq +=
static_cast<double>(line_vector[j]) * line_vector[j];
382 if (line_sq == 0.0) {
385 return cross_sq / line_sq;
392 composeRGBPixel(r, g, b, &result);
400 else if (pixel >= 255.0)
402 return static_cast<uint8_t
>(pixel);
416 Pix* color_map1, Pix* color_map2,
418 uint8_t* color1, uint8_t* color2) {
419 ASSERT_HOST(pix !=
nullptr && pixGetDepth(pix) == 32);
422 int width = pixGetWidth(pix);
423 int height = pixGetHeight(pix);
424 int left_pad = std::max(rect.
left() - 2 * factor, 0) / factor;
425 int top_pad = (rect.
top() + 2 * factor + (factor - 1)) / factor;
426 top_pad = std::min(height, top_pad);
427 int right_pad = (rect.
right() + 2 * factor + (factor - 1)) / factor;
428 right_pad = std::min(width, right_pad);
429 int bottom_pad = std::max(rect.
bottom() - 2 * factor, 0) / factor;
430 int width_pad = right_pad - left_pad;
431 int height_pad = top_pad - bottom_pad;
432 if (width_pad < 1 || height_pad < 1 || width_pad + height_pad < 4)
435 Box* scaled_box = boxCreate(left_pad, height - top_pad,
436 width_pad, height_pad);
437 Pix* scaled = pixClipRectangle(pix, scaled_box,
nullptr);
440 STATS red_stats(0, 256);
441 STATS green_stats(0, 256);
442 STATS blue_stats(0, 256);
443 uint32_t* data = pixGetData(scaled);
445 for (
int y = 0; y < height_pad; ++y) {
446 for (
int x = 0; x < width_pad; ++x, ++data) {
447 int r = GET_DATA_BYTE(data, COLOR_RED);
448 int g = GET_DATA_BYTE(data, COLOR_GREEN);
449 int b = GET_DATA_BYTE(data, COLOR_BLUE);
451 green_stats.
add(g, 1);
452 blue_stats.
add(b, 1);
459 int best_l8 =
static_cast<int>(red_stats.
ile(0.125f));
460 int best_u8 =
static_cast<int>(ceil(red_stats.
ile(0.875f)));
461 int best_i8r = best_u8 - best_l8;
462 int x_color = COLOR_RED;
463 int y1_color = COLOR_GREEN;
464 int y2_color = COLOR_BLUE;
465 int l8 =
static_cast<int>(green_stats.
ile(0.125f));
466 int u8 =
static_cast<int>(ceil(green_stats.
ile(0.875f)));
467 if (u8 - l8 > best_i8r) {
471 x_color = COLOR_GREEN;
472 y1_color = COLOR_RED;
474 l8 =
static_cast<int>(blue_stats.
ile(0.125f));
475 u8 =
static_cast<int>(ceil(blue_stats.
ile(0.875f)));
476 if (u8 - l8 > best_i8r) {
480 x_color = COLOR_BLUE;
481 y1_color = COLOR_GREEN;
482 y2_color = COLOR_RED;
487 uint32_t* data = pixGetData(scaled);
488 for (
int im_y = 0; im_y < height_pad; ++im_y) {
489 for (
int im_x = 0; im_x < width_pad; ++im_x, ++data) {
490 int x = GET_DATA_BYTE(data, x_color);
491 int y1 = GET_DATA_BYTE(data, y1_color);
492 int y2 = GET_DATA_BYTE(data, y2_color);
497 double m1 = line1.
m();
498 double c1 = line1.
c(m1);
499 double m2 = line2.
m();
500 double c2 = line2.
c(m2);
501 double rms = line1.
rms(m1, c1) + line2.
rms(m2, c2);
505 color1[y1_color] =
ClipToByte(m1 * best_l8 + c1 + 0.5);
506 color1[y2_color] =
ClipToByte(m2 * best_l8 + c2 + 0.5);
509 color2[y1_color] =
ClipToByte(m1 * best_u8 + c1 + 0.5);
510 color2[y2_color] =
ClipToByte(m2 * best_u8 + c2 + 0.5);
517 color1[L_ALPHA_CHANNEL] = 0;
518 memcpy(color2, color1, 4);
520 if (color_map1 !=
nullptr) {
521 pixSetInRectArbitrary(color_map1, scaled_box,
524 color1[COLOR_BLUE]));
525 pixSetInRectArbitrary(color_map2, scaled_box,
528 color2[COLOR_BLUE]));
529 pixSetInRectArbitrary(rms_map, scaled_box, color1[L_ALPHA_CHANNEL]);
532 boxDestroy(&scaled_box);
580 TBOX search_box(box1);
583 if (box1.
x_gap(box2) <= 0)
588 if (box1.
y_gap(box2) <= 0)
599 const FCOORD& rotation, Pix* pix) {
605 TBOX rotated_im_box(im_box);
606 rotated_im_box.
rotate(rotation);
607 Pix* rect_pix = pixCreate(box.
width(), box.
height(), 1);
608 pixRasterop(rect_pix, 0, 0, box.
width(), box.
height(),
609 PIX_SRC, pix, box.
left() - rotated_im_box.
left(),
610 rotated_im_box.
top() - box.
top());
612 pixCountPixels(rect_pix, &result,
nullptr);
613 pixDestroy(&rect_pix);
621 static void AttemptToShrinkBox(
const FCOORD& rotation,
const FCOORD& rerotation,
622 const TBOX& im_box, Pix* pix,
TBOX* slice) {
623 TBOX rotated_box(*slice);
624 rotated_box.rotate(rerotation);
625 TBOX rotated_im_box(im_box);
626 rotated_im_box.rotate(rerotation);
627 int left = rotated_box.left() - rotated_im_box.left();
628 int right = rotated_box.right() - rotated_im_box.left();
629 int top = rotated_im_box.top() - rotated_box.top();
630 int bottom = rotated_im_box.top() - rotated_box.bottom();
632 top = rotated_im_box.top() - top;
633 bottom = rotated_im_box.top() - bottom;
634 left += rotated_im_box.left();
635 right += rotated_im_box.left();
636 rotated_box.set_to_given_coords(left, bottom, right, top);
637 rotated_box.rotate(rotation);
638 slice->
set_left(rotated_box.left());
666 static void CutChunkFromParts(
const TBOX& box,
const TBOX& im_box,
668 Pix* pix, ColPartition_LIST* part_list) {
670 ColPartition_IT part_it(part_list);
672 ColPartition* part = part_it.data();
673 TBOX part_box = part->bounding_box();
680 if (box.
top() < part_box.
top()) {
681 TBOX slice(part_box);
685 AttemptToShrinkBox(rotation, rerotation, im_box, pix, &slice);
686 part_it.add_before_stay_put(
693 TBOX slice(part_box);
695 if (box.
top() < part_box.
top())
701 AttemptToShrinkBox(rotation, rerotation, im_box, pix, &slice);
702 part_it.add_before_stay_put(
709 TBOX slice(part_box);
711 if (box.
top() < part_box.
top())
717 AttemptToShrinkBox(rotation, rerotation, im_box, pix, &slice);
718 part_it.add_before_stay_put(
725 TBOX slice(part_box);
729 AttemptToShrinkBox(rotation, rerotation, im_box, pix, &slice);
730 part_it.add_before_stay_put(
736 delete part_it.extract();
739 }
while (!part_it.at_first());
749 static void DivideImageIntoParts(
const TBOX& im_box,
const FCOORD& rotation,
750 const FCOORD& rerotation, Pix* pix,
752 ColPartition_LIST* part_list) {
757 ColPartition_IT part_it(part_list);
758 part_it.add_after_then_move(pix_part);
760 rectsearch->StartRectSearch(im_box);
762 while ((part = rectsearch->NextRectSearch()) !=
nullptr) {
763 TBOX part_box = part->bounding_box();
766 for (part_it.move_to_first(); !part_it.empty(); part_it.forward()) {
767 ColPartition* pix_part = part_it.extract();
768 pix_part->DeleteBoxes();
777 if (black_area * 2 < part_box.
area() || !im_box.
contains(part_box)) {
782 part_box.
set_top(part_box.
top() + padding / 2);
784 CutChunkFromParts(part_box, im_box, rotation, rerotation,
791 if (part_list->empty()) {
799 static int ExpandImageLeft(
const TBOX& box,
int left_limit,
800 ColPartitionGrid* part_grid) {
805 while ((part =
search.NextSideSearch(
true)) !=
nullptr) {
807 const TBOX& part_box(part->bounding_box());
808 if (part_box.
y_gap(box) < 0) {
809 if (part_box.
right() > left_limit && part_box.
right() < box.
left())
810 left_limit = part_box.
right();
815 if (part !=
nullptr) {
818 search.StartRectSearch(search_box);
819 while ((part =
search.NextRectSearch()) !=
nullptr) {
821 const TBOX& part_box(part->bounding_box());
822 if (part_box.
y_gap(box) < 0) {
823 if (part_box.
right() > left_limit && part_box.
right() < box.
left()) {
824 left_limit = part_box.
right();
835 static int ExpandImageRight(
const TBOX& box,
int right_limit,
836 ColPartitionGrid* part_grid) {
841 while ((part =
search.NextSideSearch(
false)) !=
nullptr) {
843 const TBOX& part_box(part->bounding_box());
844 if (part_box.
y_gap(box) < 0) {
845 if (part_box.
left() < right_limit && part_box.
left() > box.
right())
846 right_limit = part_box.
left();
851 if (part !=
nullptr) {
854 search.StartRectSearch(search_box);
855 while ((part =
search.NextRectSearch()) !=
nullptr) {
857 const TBOX& part_box(part->bounding_box());
858 if (part_box.
y_gap(box) < 0) {
859 if (part_box.
left() < right_limit && part_box.
left() > box.
right())
860 right_limit = part_box.
left();
870 static int ExpandImageBottom(
const TBOX& box,
int bottom_limit,
871 ColPartitionGrid* part_grid) {
876 while ((part =
search.NextVerticalSearch(
true)) !=
nullptr) {
878 const TBOX& part_box(part->bounding_box());
879 if (part_box.
x_gap(box) < 0) {
880 if (part_box.
top() > bottom_limit && part_box.
top() < box.
bottom())
881 bottom_limit = part_box.
top();
886 if (part !=
nullptr) {
889 search.StartRectSearch(search_box);
890 while ((part =
search.NextRectSearch()) !=
nullptr) {
892 const TBOX& part_box(part->bounding_box());
893 if (part_box.
x_gap(box) < 0) {
894 if (part_box.
top() > bottom_limit && part_box.
top() < box.
bottom())
895 bottom_limit = part_box.
top();
905 static int ExpandImageTop(
const TBOX& box,
int top_limit,
906 ColPartitionGrid* part_grid) {
911 while ((part =
search.NextVerticalSearch(
false)) !=
nullptr) {
913 const TBOX& part_box(part->bounding_box());
914 if (part_box.
x_gap(box) < 0) {
916 top_limit = part_box.
bottom();
921 if (part !=
nullptr) {
924 search.StartRectSearch(search_box);
925 while ((part =
search.NextRectSearch()) !=
nullptr) {
927 const TBOX& part_box(part->bounding_box());
928 if (part_box.
x_gap(box) < 0) {
930 top_limit = part_box.
bottom();
943 const TBOX& limit_box,
944 ColPartitionGrid* part_grid,
TBOX* expanded_box) {
945 *expanded_box = im_box;
948 expanded_box->
set_left(ExpandImageLeft(im_box, limit_box.
left(),
952 expanded_box->
set_right(ExpandImageRight(im_box, limit_box.
right(),
956 expanded_box->
set_top(ExpandImageTop(im_box, limit_box.
top(), part_grid));
965 return expanded_box->
area() - im_box.
area();
972 static void MaximalImageBoundingBox(ColPartitionGrid* part_grid,
TBOX* im_box) {
974 memset(dunnit, 0,
sizeof(dunnit));
975 TBOX limit_box(part_grid->bleft().x(), part_grid->bleft().y(),
976 part_grid->tright().x(), part_grid->tright().y());
977 TBOX text_box(*im_box);
978 for (
int iteration = 0; iteration <
BND_COUNT; ++iteration) {
983 for (
int dir = 0; dir <
BND_COUNT; ++dir) {
987 int area_delta = ExpandImageDir(bnd, text_box, limit_box, part_grid,
988 &expanded_boxes[bnd]);
989 if (best_delta < 0 || area_delta < best_delta) {
990 best_delta = area_delta;
996 dunnit[best_dir] =
true;
997 text_box = expanded_boxes[best_dir];
1006 static void DeletePartition(ColPartition* part) {
1010 part->DeleteBoxes();
1015 part->SetBlobTypes();
1016 part->DisownBoxes();
1033 static bool ExpandImageIntoParts(
const TBOX& max_image_box,
1035 ColPartitionGrid* part_grid,
1036 ColPartition** part_ptr) {
1037 ColPartition* image_part = *part_ptr;
1038 TBOX im_part_box = image_part->bounding_box();
1040 tprintf(
"Searching for merge with image part:");
1041 im_part_box.
print();
1043 max_image_box.
print();
1045 rectsearch->StartRectSearch(max_image_box);
1047 ColPartition* best_part =
nullptr;
1049 while ((part = rectsearch->NextRectSearch()) !=
nullptr) {
1051 tprintf(
"Considering merge with part:");
1053 if (im_part_box.
contains(part->bounding_box()))
1055 else if (!max_image_box.
contains(part->bounding_box()))
1056 tprintf(
"Not within text box\n");
1066 TBOX box = part->bounding_box();
1070 rectsearch->RemoveBBox();
1071 DeletePartition(part);
1074 int x_dist = std::max(0, box.
x_gap(im_part_box));
1075 int y_dist = std::max(0, box.
y_gap(im_part_box));
1076 int dist = x_dist * x_dist + y_dist * y_dist;
1077 if (dist > box.
area() || dist > im_part_box.
area())
1079 if (best_part ==
nullptr || dist < best_dist) {
1086 if (best_part !=
nullptr) {
1088 TBOX box = best_part->bounding_box();
1090 tprintf(
"Merging image part:");
1091 im_part_box.
print();
1099 DeletePartition(image_part);
1100 part_grid->RemoveBBox(best_part);
1101 DeletePartition(best_part);
1102 rectsearch->RepositionIterator();
1110 static int IntersectArea(
const TBOX& box, ColPartition_LIST* part_list) {
1111 int intersect_area = 0;
1112 ColPartition_IT part_it(part_list);
1114 for (part_it.mark_cycle_pt(); !part_it.cycled_list();
1115 part_it.forward()) {
1116 ColPartition* image_part = part_it.data();
1118 intersect_area += intersect.
area();
1120 return intersect_area;
1128 static bool TestWeakIntersectedPart(
const TBOX& im_box,
1129 ColPartition_LIST* part_list,
1130 ColPartition* part) {
1133 const TBOX& part_box = part->bounding_box();
1135 int area = part_box.
area();
1136 int intersect_area = IntersectArea(part_box, part_list);
1137 if (area < 2 * intersect_area) {
1150 static void EliminateWeakParts(
const TBOX& im_box,
1151 ColPartitionGrid* part_grid,
1152 ColPartition_LIST* big_parts,
1153 ColPartition_LIST* part_list) {
1156 rectsearch.StartRectSearch(im_box);
1157 while ((part = rectsearch.NextRectSearch()) !=
nullptr) {
1158 if (TestWeakIntersectedPart(im_box, part_list, part)) {
1161 rectsearch.RemoveBBox();
1162 DeletePartition(part);
1168 part->SetBlobTypes();
1172 ColPartition_IT big_it(big_parts);
1173 for (big_it.mark_cycle_pt(); !big_it.cycled_list(); big_it.forward()) {
1174 part = big_it.data();
1175 if (TestWeakIntersectedPart(im_box, part_list, part)) {
1177 DeletePartition(big_it.extract());
1186 static bool ScanForOverlappingText(ColPartitionGrid* part_grid,
TBOX* box) {
1188 TBOX padded_box(*box);
1190 rectsearch.StartRectSearch(padded_box);
1192 bool any_text_in_padded_rect =
false;
1193 while ((part = rectsearch.NextRectSearch()) !=
nullptr) {
1197 any_text_in_padded_rect =
true;
1198 const TBOX& part_box = part->bounding_box();
1204 if (!any_text_in_padded_rect)
1213 static void MarkAndDeleteImageParts(
const FCOORD& rerotate,
1214 ColPartitionGrid* part_grid,
1215 ColPartition_LIST* image_parts,
1217 if (image_pix ==
nullptr)
1219 int imageheight = pixGetHeight(image_pix);
1220 ColPartition_IT part_it(image_parts);
1221 for (; !part_it.empty(); part_it.forward()) {
1222 ColPartition* part = part_it.extract();
1223 TBOX part_box = part->bounding_box();
1225 if (!ScanForOverlappingText(part_grid, &part_box) ||
1229 part_box.
rotate(rerotate);
1230 int left = part_box.
left();
1231 int top = part_box.
top();
1232 pixRasterop(image_pix, left, imageheight - top,
1233 part_box.
width(), part_box.
height(), PIX_SET,
nullptr, 0, 0);
1235 DeletePartition(part);
1250 ColPartition_LIST parts_list;
1251 ColPartition_IT part_it(&parts_list);
1258 part_it.add_after_then_move(part);
1263 MarkAndDeleteImageParts(rerotation, part_grid, &parts_list, image_mask);
1270 if (part_grid !=
nullptr)
return;
1272 gsearch.StartFullSearch();
1274 while ((part = gsearch.NextFullSearch()) !=
nullptr) {
1282 gsearch.RemoveBBox();
1283 DeletePartition(part);
1303 ColPartition_LIST* big_parts) {
1304 int imageheight = pixGetHeight(image_pix);
1310 if (boxa !=
nullptr && pixa !=
nullptr) nboxes = boxaGetCount(boxa);
1311 for (
int i = 0; i < nboxes; ++i) {
1312 l_int32 x, y, width, height;
1313 boxaGetBoxGeometry(boxa, i, &x, &y, &width, &height);
1314 Pix* pix = pixaGetPix(pixa, i, L_CLONE);
1315 TBOX im_box(x, imageheight -y - height, x + width, imageheight - y);
1319 ColPartition_LIST part_list;
1320 DivideImageIntoParts(im_box, rotation, rerotation, pix,
1321 &rectsearch, &part_list);
1323 pixa_debug->
AddPix(pix,
"ImageComponent");
1324 tprintf(
"Component has %d parts\n", part_list.length());
1327 if (!part_list.empty()) {
1328 ColPartition_IT part_it(&part_list);
1329 if (part_list.singleton()) {
1334 TBOX text_box(im_box);
1335 MaximalImageBoundingBox(part_grid, &text_box);
1336 while (ExpandImageIntoParts(text_box, &rectsearch, part_grid, &part));
1337 part_it.set_to_list(&part_list);
1338 part_it.add_after_then_move(part);
1341 EliminateWeakParts(im_box, part_grid, big_parts, &part_list);
1343 for (part_it.move_to_first(); !part_it.empty(); part_it.forward()) {
1346 part_grid->
InsertBBox(
true,
true, image_part);
1347 if (!part_it.at_last()) {
1357 DeleteSmallImages(part_grid);
static uint8_t ClipToByte(double pixel)
static void FindImagePartitions(Pix *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, Pix *image_mask)
ScrollView * MakeWindow(int x, int y, const char *window_name)
int textord_tabfind_show_images
void rotate(const FCOORD &vec)
static void ConnCompAndRectangularize(Pix *pix, DebugPixa *pixa_debug, Boxa **boxa, Pixa **pixa)
static int CountPixelsInRotatedBox(TBOX box, const TBOX &im_box, const FCOORD &rotation, Pix *pix)
static bool pixNearlyRectangular(Pix *pix, double min_fraction, double max_fraction, double max_skew_gradient, int *x_start, int *y_start, int *x_end, int *y_end)
TBOX intersection(const TBOX &box) const
int y_gap(const TBOX &box) const
BlobRegionType blob_type() const
static uint32_t ComposeRGB(uint32_t r, uint32_t g, uint32_t b)
int x_gap(const TBOX &box) const
const int kMinImageFindSize
void add(double x, double y)
GridSearch< ColPartition, ColPartition_CLIST, ColPartition_C_IT > ColPartitionGridSearch
double ile(double frac) const
void InsertBBox(bool h_spread, bool v_spread, BBC *bbox)
const double kRMSFitScaling
LIST search(LIST list, void *key, int_compare is_equal)
DLLSYM void tprintf(const char *format,...)
const double kMaxRectangularFraction
static void ComputeRectangleColors(const TBOX &rect, Pix *pix, int factor, Pix *color_map1, Pix *color_map2, Pix *rms_map, uint8_t *color1, uint8_t *color2)
void AddPartner(bool upper, ColPartition *partner)
void add(int32_t value, int32_t count)
static ColPartition * FakePartition(const TBOX &box, PolyBlockType block_type, BlobRegionType blob_type, BlobTextFlowType flow)
const TBOX & bounding_box() const
bool overlap(const TBOX &box) const
const double kMinRectangularFraction
static double ColorDistanceFromLine(const uint8_t *line1, const uint8_t *line2, const uint8_t *point)
double rms(double m, double c) const
bool contains(const FCOORD pt) const
static bool BlankImageInBetween(const TBOX &box1, const TBOX &box2, const TBOX &im_box, const FCOORD &rotation, Pix *pix)
static bool BoundsWithinRect(Pix *pix, int *x_start, int *y_start, int *x_end, int *y_end)
static Pix * FindImages(Pix *pix, DebugPixa *pixa_debug)
void AddPix(const Pix *pix, const char *caption)
void SetUniqueMode(bool mode)
const int kMinColorDifference
const double kMaxRectangularGradient
void DisplayBoxes(ScrollView *window)
#define INT_VAR(name, val, comment)