22 #pragma warning(disable:4244) // Conversion warnings
35 #include "config_auto.h"
37 #include "allheaders.h"
70 return pixCreate(pixGetWidth(pix), pixGetHeight(pix), 1);
72 Pix *pixr = pixReduceRankBinaryCascade(pix, 1, 0, 0, 0);
77 Pix *pixht2 = pixGenHalftoneMask(pixr,
NULL, &ht_found,
80 if (!ht_found && pixht2 !=
NULL)
83 return pixCreate(pixGetWidth(pix), pixGetHeight(pix), 1);
86 Pix *pixht = pixExpandReplicate(pixht2, 2);
91 Pix *pixt = pixSeedfillBinary(
NULL, pixht, pix, 8);
92 pixOr(pixht, pixht, pixt);
96 Pix* pixfinemask = pixReduceRankBinaryCascade(pixht, 1, 1, 3, 3);
97 pixDilateBrick(pixfinemask, pixfinemask, 5, 5);
99 Pix* pixreduced = pixReduceRankBinaryCascade(pixht, 1, 1, 1, 1);
100 Pix* pixreduced2 = pixReduceRankBinaryCascade(pixreduced, 3, 3, 3, 0);
101 pixDestroy(&pixreduced);
102 pixDilateBrick(pixreduced2, pixreduced2, 5, 5);
103 Pix* pixcoarsemask = pixExpandReplicate(pixreduced2, 8);
104 pixDestroy(&pixreduced2);
107 pixAnd(pixcoarsemask, pixcoarsemask, pixfinemask);
108 pixDestroy(&pixfinemask);
110 pixDilateBrick(pixcoarsemask, pixcoarsemask, 3, 3);
111 Pix* pixmask = pixExpandReplicate(pixcoarsemask, 16);
112 pixDestroy(&pixcoarsemask);
114 pixWrite(
"junkexpandedcoarsemask.png", pixmask, IFF_PNG);
116 pixAnd(pixht, pixht, pixmask);
117 pixDestroy(&pixmask);
119 pixWrite(
"junkfinalimagemask.png", pixht, IFF_PNG);
121 Pix* result = pixCreate(pixGetWidth(pix), pixGetHeight(pix), 1);
122 pixOr(result, result, pixht);
139 pixWrite(
"junkconncompimage.png", pix, IFF_PNG);
141 *boxa = pixConnComp(pix, pixa, 8);
145 int npixes = pixaGetCount(*pixa);
146 for (
int i = 0; i < npixes; ++i) {
147 int x_start, x_end, y_start, y_end;
148 Pix* img_pix = pixaGetPix(*pixa, i, L_CLONE);
153 &x_start, &y_start, &x_end, &y_end)) {
154 Pix* simple_pix = pixCreate(x_end - x_start, y_end - y_start, 1);
155 pixSetAll(simple_pix);
156 pixDestroy(&img_pix);
158 pixaReplacePix(*pixa, i, simple_pix,
NULL);
159 img_pix = pixaGetPix(*pixa, i, L_CLONE);
161 l_int32 x, y, width, height;
162 boxaGetBoxGeometry(*boxa, i, &x, &y, &width, &height);
163 Box* simple_box = boxCreate(x + x_start, y + y_start,
164 x_end - x_start, y_end - y_start);
165 boxaReplaceBox(*boxa, i, simple_box);
167 pixDestroy(&img_pix);
178 static bool HScanForEdge(
uinT32* data,
int wpl,
int x_start,
int x_end,
179 int min_count,
int mid_width,
int max_count,
180 int y_end,
int y_step,
int* y_start) {
182 for (
int y = *y_start; y != y_end; y += y_step) {
185 uinT32* line = data + wpl * y;
186 for (
int x = x_start; x < x_end; ++x) {
187 if (GET_DATA_BIT(line, x))
190 if (mid_rows == 0 && pix_count < min_count)
194 if (pix_count > max_count)
197 if (mid_rows > mid_width)
210 static bool VScanForEdge(
uinT32* data,
int wpl,
int y_start,
int y_end,
211 int min_count,
int mid_width,
int max_count,
212 int x_end,
int x_step,
int* x_start) {
214 for (
int x = *x_start; x != x_end; x += x_step) {
216 uinT32* line = data + y_start * wpl;
217 for (
int y = y_start; y < y_end; ++y, line += wpl) {
218 if (GET_DATA_BIT(line, x))
221 if (mid_cols == 0 && pix_count < min_count)
225 if (pix_count > max_count)
228 if (mid_cols > mid_width)
244 double min_fraction,
double max_fraction,
245 double max_skew_gradient,
246 int* x_start,
int* y_start,
247 int* x_end,
int* y_end) {
250 *x_end = pixGetWidth(pix);
252 *y_end = pixGetHeight(pix);
254 uinT32* data = pixGetData(pix);
255 int wpl = pixGetWpl(pix);
256 bool any_cut =
false;
257 bool left_done =
false;
258 bool right_done =
false;
259 bool top_done =
false;
260 bool bottom_done =
false;
264 int width = *x_end - *x_start;
265 int min_count =
static_cast<int>(width * min_fraction);
266 int max_count =
static_cast<int>(width * max_fraction);
267 int edge_width =
static_cast<int>(width * max_skew_gradient);
268 if (HScanForEdge(data, wpl, *x_start, *x_end, min_count, edge_width,
269 max_count, *y_end, 1, y_start) && !top_done) {
274 if (HScanForEdge(data, wpl, *x_start, *x_end, min_count, edge_width,
275 max_count, *y_start, -1, y_end) && !bottom_done) {
282 int height = *y_end - *y_start;
283 min_count =
static_cast<int>(height * min_fraction);
284 max_count =
static_cast<int>(height * max_fraction);
285 edge_width =
static_cast<int>(height * max_skew_gradient);
286 if (VScanForEdge(data, wpl, *y_start, *y_end, min_count, edge_width,
287 max_count, *x_end, 1, x_start) && !left_done) {
292 if (VScanForEdge(data, wpl, *y_start, *y_end, min_count, edge_width,
293 max_count, *x_start, -1, x_end) && !right_done) {
302 return left_done && right_done && top_done && bottom_done;
310 int* x_end,
int* y_end) {
311 Box* input_box = boxCreate(*x_start, *y_start, *x_end - *x_start,
313 Box* output_box =
NULL;
314 pixClipBoxToForeground(pix, input_box,
NULL, &output_box);
315 bool result = output_box !=
NULL;
317 l_int32 x, y, width, height;
318 boxGetGeometry(output_box, &x, &y, &width, &height);
323 boxDestroy(&output_box);
325 boxDestroy(&input_box);
334 const uinT8* point) {
338 line_vector[i] =
static_cast<int>(line2[i]) - static_cast<int>(line1[i]);
339 point_vector[i] =
static_cast<int>(point[i]) - static_cast<int>(line1[i]);
341 line_vector[L_ALPHA_CHANNEL] = 0;
344 cross[COLOR_RED] = line_vector[COLOR_GREEN] * point_vector[COLOR_BLUE]
345 - line_vector[COLOR_BLUE] * point_vector[COLOR_GREEN];
346 cross[COLOR_GREEN] = line_vector[COLOR_BLUE] * point_vector[COLOR_RED]
347 - line_vector[COLOR_RED] * point_vector[COLOR_BLUE];
348 cross[COLOR_BLUE] = line_vector[COLOR_RED] * point_vector[COLOR_GREEN]
349 - line_vector[COLOR_GREEN] * point_vector[COLOR_RED];
350 cross[L_ALPHA_CHANNEL] = 0;
352 double cross_sq = 0.0;
353 double line_sq = 0.0;
355 cross_sq +=
static_cast<double>(cross[j]) * cross[j];
356 line_sq +=
static_cast<double>(line_vector[j]) * line_vector[j];
358 if (line_sq == 0.0) {
361 return cross_sq / line_sq;
368 composeRGBPixel(r, g, b, &result);
376 else if (pixel >= 255.0)
378 return static_cast<uinT8>(pixel);
392 Pix* color_map1, Pix* color_map2,
398 int width = pixGetWidth(pix);
399 int height = pixGetHeight(pix);
400 int left_pad =
MAX(rect.
left() - 2 * factor, 0) / factor;
401 int top_pad = (rect.
top() + 2 * factor + (factor - 1)) / factor;
402 top_pad =
MIN(height, top_pad);
403 int right_pad = (rect.
right() + 2 * factor + (factor - 1)) / factor;
404 right_pad =
MIN(width, right_pad);
405 int bottom_pad =
MAX(rect.
bottom() - 2 * factor, 0) / factor;
406 int width_pad = right_pad - left_pad;
407 int height_pad = top_pad - bottom_pad;
408 if (width_pad < 1 || height_pad < 1 || width_pad + height_pad < 4)
411 Box* scaled_box = boxCreate(left_pad, height - top_pad,
412 width_pad, height_pad);
413 Pix* scaled = pixClipRectangle(pix, scaled_box,
NULL);
416 STATS red_stats(0, 256);
417 STATS green_stats(0, 256);
418 STATS blue_stats(0, 256);
419 uinT32* data = pixGetData(scaled);
421 for (
int y = 0; y < height_pad; ++y) {
422 for (
int x = 0; x < width_pad; ++x, ++data) {
423 int r = GET_DATA_BYTE(data, COLOR_RED);
424 int g = GET_DATA_BYTE(data, COLOR_GREEN);
425 int b = GET_DATA_BYTE(data, COLOR_BLUE);
427 green_stats.
add(g, 1);
428 blue_stats.
add(b, 1);
435 int best_l8 =
static_cast<int>(red_stats.
ile(0.125
f));
436 int best_u8 =
static_cast<int>(ceil(red_stats.
ile(0.875f)));
437 int best_i8r = best_u8 - best_l8;
438 int x_color = COLOR_RED;
439 int y1_color = COLOR_GREEN;
440 int y2_color = COLOR_BLUE;
441 int l8 =
static_cast<int>(green_stats.
ile(0.125
f));
442 int u8 =
static_cast<int>(ceil(green_stats.
ile(0.875f)));
443 if (u8 - l8 > best_i8r) {
447 x_color = COLOR_GREEN;
448 y1_color = COLOR_RED;
450 l8 =
static_cast<int>(blue_stats.
ile(0.125
f));
451 u8 =
static_cast<int>(ceil(blue_stats.
ile(0.875f)));
452 if (u8 - l8 > best_i8r) {
456 x_color = COLOR_BLUE;
457 y1_color = COLOR_GREEN;
458 y2_color = COLOR_RED;
463 uinT32* data = pixGetData(scaled);
464 for (
int im_y = 0; im_y < height_pad; ++im_y) {
465 for (
int im_x = 0; im_x < width_pad; ++im_x, ++data) {
466 int x = GET_DATA_BYTE(data, x_color);
467 int y1 = GET_DATA_BYTE(data, y1_color);
468 int y2 = GET_DATA_BYTE(data, y2_color);
473 double m1 = line1.
m();
474 double c1 = line1.
c(m1);
475 double m2 = line2.
m();
476 double c2 = line2.
c(m2);
477 double rms = line1.
rms(m1, c1) + line2.
rms(m2, c2);
481 color1[y1_color] =
ClipToByte(m1 * best_l8 + c1 + 0.5);
482 color1[y2_color] =
ClipToByte(m2 * best_l8 + c2 + 0.5);
485 color2[y1_color] =
ClipToByte(m1 * best_u8 + c1 + 0.5);
486 color2[y2_color] =
ClipToByte(m2 * best_u8 + c2 + 0.5);
493 color1[L_ALPHA_CHANNEL] = 0;
494 memcpy(color2, color1, 4);
496 if (color_map1 !=
NULL) {
497 pixSetInRectArbitrary(color_map1, scaled_box,
500 color1[COLOR_BLUE]));
501 pixSetInRectArbitrary(color_map2, scaled_box,
504 color2[COLOR_BLUE]));
505 pixSetInRectArbitrary(rms_map, scaled_box, color1[L_ALPHA_CHANNEL]);
508 boxDestroy(&scaled_box);
556 TBOX search_box(box1);
559 if (box1.
x_gap(box2) <= 0)
564 if (box1.
y_gap(box2) <= 0)
575 const FCOORD& rotation, Pix* pix) {
581 TBOX rotated_im_box(im_box);
582 rotated_im_box.
rotate(rotation);
583 Pix* rect_pix = pixCreate(box.
width(), box.
height(), 1);
584 pixRasterop(rect_pix, 0, 0, box.
width(), box.
height(),
585 PIX_SRC, pix, box.
left() - rotated_im_box.
left(),
586 rotated_im_box.
top() - box.
top());
588 pixCountPixels(rect_pix, &result,
NULL);
589 pixDestroy(&rect_pix);
597 static void AttemptToShrinkBox(
const FCOORD& rotation,
const FCOORD& rerotation,
598 const TBOX& im_box, Pix* pix,
TBOX* slice) {
599 TBOX rotated_box(*slice);
600 rotated_box.rotate(rerotation);
601 TBOX rotated_im_box(im_box);
602 rotated_im_box.rotate(rerotation);
603 int left = rotated_box.left() - rotated_im_box.left();
604 int right = rotated_box.right() - rotated_im_box.left();
605 int top = rotated_im_box.top() - rotated_box.top();
606 int bottom = rotated_im_box.top() - rotated_box.bottom();
608 top = rotated_im_box.top() - top;
609 bottom = rotated_im_box.top() - bottom;
610 left += rotated_im_box.left();
611 right += rotated_im_box.left();
612 rotated_box.set_to_given_coords(left, bottom, right, top);
613 rotated_box.rotate(rotation);
614 slice->
set_left(rotated_box.left());
642 static void CutChunkFromParts(
const TBOX& box,
const TBOX& im_box,
644 Pix* pix, ColPartition_LIST* part_list) {
646 ColPartition_IT part_it(part_list);
648 ColPartition* part = part_it.data();
649 TBOX part_box = part->bounding_box();
656 if (box.
top() < part_box.
top()) {
657 TBOX slice(part_box);
661 AttemptToShrinkBox(rotation, rerotation, im_box, pix, &slice);
662 part_it.add_before_stay_put(
669 TBOX slice(part_box);
671 if (box.
top() < part_box.
top())
677 AttemptToShrinkBox(rotation, rerotation, im_box, pix, &slice);
678 part_it.add_before_stay_put(
685 TBOX slice(part_box);
687 if (box.
top() < part_box.
top())
693 AttemptToShrinkBox(rotation, rerotation, im_box, pix, &slice);
694 part_it.add_before_stay_put(
701 TBOX slice(part_box);
705 AttemptToShrinkBox(rotation, rerotation, im_box, pix, &slice);
706 part_it.add_before_stay_put(
712 delete part_it.extract();
715 }
while (!part_it.at_first());
725 static void DivideImageIntoParts(
const TBOX& im_box,
const FCOORD& rotation,
726 const FCOORD& rerotation, Pix* pix,
728 ColPartition_LIST* part_list) {
733 ColPartition_IT part_it(part_list);
734 part_it.add_after_then_move(pix_part);
736 rectsearch->StartRectSearch(im_box);
738 while ((part = rectsearch->NextRectSearch()) !=
NULL) {
739 TBOX part_box = part->bounding_box();
742 for (part_it.move_to_first(); !part_it.empty(); part_it.forward()) {
743 ColPartition* pix_part = part_it.extract();
744 pix_part->DeleteBoxes();
753 if (black_area * 2 < part_box.
area() || !im_box.
contains(part_box)) {
758 part_box.
set_top(part_box.
top() + padding / 2);
760 CutChunkFromParts(part_box, im_box, rotation, rerotation,
767 if (part_list->empty()) {
775 static int ExpandImageLeft(
const TBOX& box,
int left_limit,
776 ColPartitionGrid* part_grid) {
781 while ((part =
search.NextSideSearch(
true)) !=
NULL) {
783 const TBOX& part_box(part->bounding_box());
784 if (part_box.
y_gap(box) < 0) {
785 if (part_box.
right() > left_limit && part_box.
right() < box.
left())
786 left_limit = part_box.
right();
794 search.StartRectSearch(search_box);
795 while ((part =
search.NextRectSearch()) !=
NULL) {
797 const TBOX& part_box(part->bounding_box());
798 if (part_box.
y_gap(box) < 0) {
799 if (part_box.
right() > left_limit && part_box.
right() < box.
left()) {
800 left_limit = part_box.
right();
811 static int ExpandImageRight(
const TBOX& box,
int right_limit,
812 ColPartitionGrid* part_grid) {
817 while ((part =
search.NextSideSearch(
false)) !=
NULL) {
819 const TBOX& part_box(part->bounding_box());
820 if (part_box.
y_gap(box) < 0) {
821 if (part_box.
left() < right_limit && part_box.
left() > box.
right())
822 right_limit = part_box.
left();
830 search.StartRectSearch(search_box);
831 while ((part =
search.NextRectSearch()) !=
NULL) {
833 const TBOX& part_box(part->bounding_box());
834 if (part_box.
y_gap(box) < 0) {
835 if (part_box.
left() < right_limit && part_box.
left() > box.
right())
836 right_limit = part_box.
left();
846 static int ExpandImageBottom(
const TBOX& box,
int bottom_limit,
847 ColPartitionGrid* part_grid) {
852 while ((part =
search.NextVerticalSearch(
true)) !=
NULL) {
854 const TBOX& part_box(part->bounding_box());
855 if (part_box.
x_gap(box) < 0) {
856 if (part_box.
top() > bottom_limit && part_box.
top() < box.
bottom())
857 bottom_limit = part_box.
top();
865 search.StartRectSearch(search_box);
866 while ((part =
search.NextRectSearch()) !=
NULL) {
868 const TBOX& part_box(part->bounding_box());
869 if (part_box.
x_gap(box) < 0) {
870 if (part_box.
top() > bottom_limit && part_box.
top() < box.
bottom())
871 bottom_limit = part_box.
top();
881 static int ExpandImageTop(
const TBOX& box,
int top_limit,
882 ColPartitionGrid* part_grid) {
887 while ((part =
search.NextVerticalSearch(
false)) !=
NULL) {
889 const TBOX& part_box(part->bounding_box());
890 if (part_box.
x_gap(box) < 0) {
892 top_limit = part_box.
bottom();
900 search.StartRectSearch(search_box);
901 while ((part =
search.NextRectSearch()) !=
NULL) {
903 const TBOX& part_box(part->bounding_box());
904 if (part_box.
x_gap(box) < 0) {
906 top_limit = part_box.
bottom();
919 const TBOX& limit_box,
920 ColPartitionGrid* part_grid,
TBOX* expanded_box) {
921 *expanded_box = im_box;
924 expanded_box->
set_left(ExpandImageLeft(im_box, limit_box.
left(),
928 expanded_box->
set_right(ExpandImageRight(im_box, limit_box.
right(),
932 expanded_box->
set_top(ExpandImageTop(im_box, limit_box.
top(), part_grid));
941 return expanded_box->
area() - im_box.
area();
948 static void MaximalImageBoundingBox(ColPartitionGrid* part_grid,
TBOX* im_box) {
950 memset(dunnit, 0,
sizeof(dunnit));
951 TBOX limit_box(part_grid->bleft().x(), part_grid->bleft().y(),
952 part_grid->tright().x(), part_grid->tright().y());
953 TBOX text_box(*im_box);
954 for (
int iteration = 0; iteration <
BND_COUNT; ++iteration) {
959 for (
int dir = 0; dir <
BND_COUNT; ++dir) {
963 int area_delta = ExpandImageDir(bnd, text_box, limit_box, part_grid,
964 &expanded_boxes[bnd]);
965 if (best_delta < 0 || area_delta < best_delta) {
966 best_delta = area_delta;
972 dunnit[best_dir] =
true;
973 text_box = expanded_boxes[best_dir];
982 static void DeletePartition(ColPartition* part) {
991 part->SetBlobTypes();
1009 static bool ExpandImageIntoParts(
const TBOX& max_image_box,
1011 ColPartitionGrid* part_grid,
1012 ColPartition** part_ptr) {
1013 ColPartition* image_part = *part_ptr;
1014 TBOX im_part_box = image_part->bounding_box();
1016 tprintf(
"Searching for merge with image part:");
1017 im_part_box.
print();
1019 max_image_box.
print();
1021 rectsearch->StartRectSearch(max_image_box);
1023 ColPartition* best_part =
NULL;
1025 while ((part = rectsearch->NextRectSearch()) !=
NULL) {
1027 tprintf(
"Considering merge with part:");
1029 if (im_part_box.
contains(part->bounding_box()))
1031 else if (!max_image_box.
contains(part->bounding_box()))
1032 tprintf(
"Not within text box\n");
1042 TBOX box = part->bounding_box();
1046 rectsearch->RemoveBBox();
1047 DeletePartition(part);
1050 int x_dist =
MAX(0, box.
x_gap(im_part_box));
1051 int y_dist =
MAX(0, box.
y_gap(im_part_box));
1052 int dist = x_dist * x_dist + y_dist * y_dist;
1053 if (dist > box.
area() || dist > im_part_box.
area())
1055 if (best_part ==
NULL || dist < best_dist) {
1062 if (best_part !=
NULL) {
1064 TBOX box = best_part->bounding_box();
1066 tprintf(
"Merging image part:");
1067 im_part_box.
print();
1075 DeletePartition(image_part);
1076 part_grid->RemoveBBox(best_part);
1077 DeletePartition(best_part);
1078 rectsearch->RepositionIterator();
1086 static int IntersectArea(
const TBOX& box, ColPartition_LIST* part_list) {
1087 int intersect_area = 0;
1088 ColPartition_IT part_it(part_list);
1090 for (part_it.mark_cycle_pt(); !part_it.cycled_list();
1091 part_it.forward()) {
1092 ColPartition* image_part = part_it.data();
1094 intersect_area += intersect.
area();
1096 return intersect_area;
1104 static bool TestWeakIntersectedPart(
const TBOX& im_box,
1105 ColPartition_LIST* part_list,
1106 ColPartition* part) {
1109 TBOX part_box = part->bounding_box();
1111 int area = part_box.
area();
1112 int intersect_area = IntersectArea(part_box, part_list);
1113 if (area < 2 * intersect_area) {
1126 static void EliminateWeakParts(
const TBOX& im_box,
1127 ColPartitionGrid* part_grid,
1128 ColPartition_LIST* big_parts,
1129 ColPartition_LIST* part_list) {
1132 rectsearch.StartRectSearch(im_box);
1133 while ((part = rectsearch.NextRectSearch()) !=
NULL) {
1134 if (TestWeakIntersectedPart(im_box, part_list, part)) {
1137 rectsearch.RemoveBBox();
1138 DeletePartition(part);
1144 part->SetBlobTypes();
1148 ColPartition_IT big_it(big_parts);
1149 for (big_it.mark_cycle_pt(); !big_it.cycled_list(); big_it.forward()) {
1150 part = big_it.data();
1151 if (TestWeakIntersectedPart(im_box, part_list, part)) {
1153 DeletePartition(big_it.extract());
1162 static bool ScanForOverlappingText(ColPartitionGrid* part_grid,
TBOX* box) {
1164 TBOX padded_box(*box);
1166 rectsearch.StartRectSearch(padded_box);
1168 bool any_text_in_padded_rect =
false;
1169 while ((part = rectsearch.NextRectSearch()) !=
NULL) {
1173 any_text_in_padded_rect =
true;
1174 TBOX part_box = part->bounding_box();
1180 if (!any_text_in_padded_rect)
1189 static void MarkAndDeleteImageParts(
const FCOORD& rerotate,
1190 ColPartitionGrid* part_grid,
1191 ColPartition_LIST* image_parts,
1193 if (image_pix ==
NULL)
1195 int imageheight = pixGetHeight(image_pix);
1196 ColPartition_IT part_it(image_parts);
1197 for (; !part_it.empty(); part_it.forward()) {
1198 ColPartition* part = part_it.extract();
1199 TBOX part_box = part->bounding_box();
1201 if (!ScanForOverlappingText(part_grid, &part_box) ||
1205 part_box.
rotate(rerotate);
1206 int left = part_box.
left();
1207 int top = part_box.
top();
1208 pixRasterop(image_pix, left, imageheight - top,
1211 DeletePartition(part);
1226 ColPartition_LIST parts_list;
1227 ColPartition_IT part_it(&parts_list);
1234 part_it.add_after_then_move(part);
1239 MarkAndDeleteImageParts(rerotation, part_grid, &parts_list, image_mask);
1246 if (part_grid !=
NULL)
return;
1248 gsearch.StartFullSearch();
1250 while ((part = gsearch.NextFullSearch()) !=
NULL) {
1258 gsearch.RemoveBBox();
1259 DeletePartition(part);
1277 const FCOORD& rerotation,
1281 ColPartition_LIST* big_parts) {
1282 int imageheight = pixGetHeight(image_pix);
1287 int nboxes = boxaGetCount(boxa);
1288 for (
int i = 0; i < nboxes; ++i) {
1289 l_int32 x, y, width, height;
1290 boxaGetBoxGeometry(boxa, i, &x, &y, &width, &height);
1291 Pix* pix = pixaGetPix(pixa, i, L_CLONE);
1292 TBOX im_box(x, imageheight -y - height, x + width, imageheight - y);
1296 ColPartition_LIST part_list;
1297 DivideImageIntoParts(im_box, rotation, rerotation, pix,
1298 &rectsearch, &part_list);
1300 pixWrite(
"junkimagecomponent.png", pix, IFF_PNG);
1301 tprintf(
"Component has %d parts\n", part_list.length());
1304 if (!part_list.empty()) {
1305 ColPartition_IT part_it(&part_list);
1306 if (part_list.singleton()) {
1311 TBOX text_box(im_box);
1312 MaximalImageBoundingBox(part_grid, &text_box);
1313 while (ExpandImageIntoParts(text_box, &rectsearch, part_grid, &part));
1314 part_it.set_to_list(&part_list);
1315 part_it.add_after_then_move(part);
1318 EliminateWeakParts(im_box, part_grid, big_parts, &part_list);
1320 for (part_it.move_to_first(); !part_it.empty(); part_it.forward()) {
1323 part_grid->
InsertBBox(
true,
true, image_part);
1324 if (!part_it.at_last()) {
1334 DeleteSmallImages(part_grid);