15 #include "config_auto.h"
19 #include "allheaders.h"
48 : x_origin_(0), y_origin_(0), pix_(nullptr) {
51 if (scale_factor_ < 1) scale_factor_ = 1;
68 TBOX image_box(0, 0, pixGetWidth(nontext_map), pixGetHeight(nontext_map));
70 y_origin_ = image_box.
height();
71 int width = (image_box.
width() + scale_factor_ - 1) / scale_factor_;
72 int height = (image_box.
height() + scale_factor_ - 1) / scale_factor_;
74 pix_ = pixCreate(width, height, 8);
75 ProjectBlobs(&input_block->
blobs, rotation, image_box, nontext_map);
76 ProjectBlobs(&input_block->
large_blobs, rotation, image_box, nontext_map);
77 Pix* final_pix = pixBlockconv(pix_, 1, 1);
86 #ifndef GRAPHICS_DISABLED
87 BLOBNBOX_IT it(blobs);
88 for (it.mark_cycle_pt(); !it.cycled_list(); it.forward()) {
99 #endif // GRAPHICS_DISABLED
107 BLOBNBOX_LIST* blobs, BLOBNBOX_LIST* small_blobs)
const {
108 BLOBNBOX_IT it(blobs);
109 BLOBNBOX_IT small_it(small_blobs);
110 for (it.mark_cycle_pt(); !it.cycled_list(); it.forward()) {
117 small_it.add_to_end(it.extract());
124 #ifndef GRAPHICS_DISABLED
125 int width = pixGetWidth(pix_);
126 int height = pixGetHeight(pix_);
127 Pix* pixc = pixCreate(width, height, 32);
128 int src_wpl = pixGetWpl(pix_);
129 int col_wpl = pixGetWpl(pixc);
130 uint32_t* src_data = pixGetData(pix_);
131 uint32_t* col_data = pixGetData(pixc);
132 for (
int y = 0; y < height; ++y, src_data += src_wpl, col_data += col_wpl) {
133 for (
int x = 0; x < width; ++x) {
134 int pixel = GET_DATA_BYTE(src_data, x);
137 composeRGBPixel(0, 0, pixel * 15, &result);
138 else if (pixel <= 145)
139 composeRGBPixel(0, (pixel - 17) * 2, 255, &result);
141 composeRGBPixel((pixel - 145) * 2, 255, 255, &result);
142 col_data[x] = result;
145 auto* win =
new ScrollView(
"Projection", 0, 0,
146 width, height, width, height);
147 win->Image(pixc, 0, 0);
150 #endif // GRAPHICS_DISABLED
199 bool horizontal_textline,
204 int parallel_gap = 0;
209 if (horizontal_textline) {
210 parallel_gap = from_box.
x_gap(to_box) + from_box.
width();
211 start_pt.
x = (from_box.
left() + from_box.
right()) / 2;
212 end_pt.
x = start_pt.
x;
214 start_pt.
y = from_box.
top();
215 end_pt.
y = std::min(to_box.
top(), start_pt.
y);
217 start_pt.
y = from_box.
bottom();
218 end_pt.
y = std::max(to_box.
bottom(), start_pt.
y);
221 parallel_gap = from_box.
y_gap(to_box) + from_box.
height();
223 start_pt.
x = from_box.
right();
224 end_pt.
x = std::min(to_box.
right(), start_pt.
x);
226 start_pt.
x = from_box.
left();
227 end_pt.
x = std::max(to_box.
left(), start_pt.
x);
229 start_pt.
y = (from_box.
bottom() + from_box.
top()) / 2;
230 end_pt.
y = start_pt.
y;
236 int perpendicular_gap = 0;
240 if (start_pt.
x != end_pt.
x || start_pt.
y != end_pt.
y) {
241 if (denorm !=
nullptr) {
246 if (abs(start_pt.
y - end_pt.
y) >= abs(start_pt.
x - end_pt.
x)) {
277 int y1,
int y2)
const {
278 x = ImageXToProjectionX(x);
279 y1 = ImageYToProjectionY(y1);
280 y2 = ImageYToProjectionY(y2);
281 if (y1 == y2)
return 0;
282 int wpl = pixGetWpl(pix_);
283 int step = y1 < y2 ? 1 : -1;
284 uint32_t* data = pixGetData(pix_) + y1 * wpl;
286 int prev_pixel = GET_DATA_BYTE(data, x);
288 int right_way_steps = 0;
289 for (
int y = y1; y != y2; y += step) {
291 int pixel = GET_DATA_BYTE(data, x);
293 tprintf(
"At (%d,%d), pix = %d, prev=%d\n",
294 x, y + step, pixel, prev_pixel);
295 if (pixel < prev_pixel)
297 else if (pixel > prev_pixel)
311 x1 = ImageXToProjectionX(x1);
312 x2 = ImageXToProjectionX(x2);
313 y = ImageYToProjectionY(y);
314 if (x1 == x2)
return 0;
315 int wpl = pixGetWpl(pix_);
316 int step = x1 < x2 ? 1 : -1;
317 uint32_t* data = pixGetData(pix_) + y * wpl;
318 int prev_pixel = GET_DATA_BYTE(data, x1);
320 int right_way_steps = 0;
321 for (
int x = x1; x != x2; x += step) {
322 int pixel = GET_DATA_BYTE(data, x + step);
324 tprintf(
"At (%d,%d), pix = %d, prev=%d\n",
325 x + step, y, pixel, prev_pixel);
326 if (pixel < prev_pixel)
328 else if (pixel > prev_pixel)
346 EvaluateBoxInternal(box, denorm, debug, &grad1, &grad2,
nullptr,
nullptr);
347 int worst_result = std::min(grad1, grad2);
348 int total_result = grad1 + grad2;
349 if (total_result >= 6)
return false;
352 if (worst_result < 0)
381 tprintf(
"Partition hresult=%d, vresult=%d from:", hresult, vresult);
385 return hresult >= -vresult ? hresult : vresult;
415 return EvaluateBoxInternal(box, denorm, debug,
nullptr,
nullptr,
nullptr,
nullptr);
421 int TextlineProjection::EvaluateBoxInternal(
const TBOX& box,
422 const DENORM* denorm,
bool debug,
423 int* hgrad1,
int* hgrad2,
424 int* vgrad1,
int* vgrad2)
const {
425 int top_gradient = BestMeanGradientInRow(denorm, box.
left(), box.
right(),
427 int bottom_gradient = -BestMeanGradientInRow(denorm, box.
left(), box.
right(),
429 int left_gradient = BestMeanGradientInColumn(denorm, box.
left(), box.
bottom(),
431 int right_gradient = -BestMeanGradientInColumn(denorm, box.
right(),
434 int top_clipped = std::max(top_gradient, 0);
435 int bottom_clipped = std::max(bottom_gradient, 0);
436 int left_clipped = std::max(left_gradient, 0);
437 int right_clipped = std::max(right_gradient, 0);
439 tprintf(
"Gradients: top = %d, bottom = %d, left= %d, right= %d for box:",
440 top_gradient, bottom_gradient, left_gradient, right_gradient);
443 int result = std::max(top_clipped, bottom_clipped) -
444 std::max(left_clipped, right_clipped);
445 if (hgrad1 !=
nullptr && hgrad2 !=
nullptr) {
446 *hgrad1 = top_gradient;
447 *hgrad2 = bottom_gradient;
449 if (vgrad1 !=
nullptr && vgrad2 !=
nullptr) {
450 *vgrad1 = left_gradient;
451 *vgrad2 = right_gradient;
461 int TextlineProjection::BestMeanGradientInRow(
const DENORM* denorm,
462 int16_t min_x, int16_t max_x, int16_t y,
463 bool best_is_max)
const {
464 TPOINT start_pt(min_x, y);
466 int upper = MeanPixelsInLineSegment(denorm, -2, start_pt, end_pt);
467 int lower = MeanPixelsInLineSegment(denorm, 2, start_pt, end_pt);
468 int best_gradient = lower - upper;
469 upper = MeanPixelsInLineSegment(denorm, -1, start_pt, end_pt);
470 lower = MeanPixelsInLineSegment(denorm, 3, start_pt, end_pt);
471 int gradient = lower - upper;
472 if ((gradient > best_gradient) == best_is_max)
473 best_gradient = gradient;
474 upper = MeanPixelsInLineSegment(denorm, -3, start_pt, end_pt);
475 lower = MeanPixelsInLineSegment(denorm, 1, start_pt, end_pt);
476 gradient = lower - upper;
477 if ((gradient > best_gradient) == best_is_max)
478 best_gradient = gradient;
479 return best_gradient;
488 int TextlineProjection::BestMeanGradientInColumn(
const DENORM* denorm, int16_t x,
489 int16_t min_y, int16_t max_y,
490 bool best_is_max)
const {
491 TPOINT start_pt(x, min_y);
493 int left = MeanPixelsInLineSegment(denorm, -2, start_pt, end_pt);
494 int right = MeanPixelsInLineSegment(denorm, 2, start_pt, end_pt);
495 int best_gradient = right - left;
496 left = MeanPixelsInLineSegment(denorm, -1, start_pt, end_pt);
497 right = MeanPixelsInLineSegment(denorm, 3, start_pt, end_pt);
498 int gradient = right - left;
499 if ((gradient > best_gradient) == best_is_max)
500 best_gradient = gradient;
501 left = MeanPixelsInLineSegment(denorm, -3, start_pt, end_pt);
502 right = MeanPixelsInLineSegment(denorm, 1, start_pt, end_pt);
503 gradient = right - left;
504 if ((gradient > best_gradient) == best_is_max)
505 best_gradient = gradient;
506 return best_gradient;
519 int TextlineProjection::MeanPixelsInLineSegment(
const DENORM* denorm,
523 TransformToPixCoords(denorm, &start_pt);
524 TransformToPixCoords(denorm, &end_pt);
525 TruncateToImageBounds(&start_pt);
526 TruncateToImageBounds(&end_pt);
527 int wpl = pixGetWpl(pix_);
528 uint32_t* data = pixGetData(pix_);
531 int x_delta = end_pt.
x - start_pt.
x;
532 int y_delta = end_pt.
y - start_pt.
y;
533 if (abs(x_delta) >= abs(y_delta)) {
537 int x_step = x_delta > 0 ? 1 : -1;
540 start_pt.
y += offset;
542 TruncateToImageBounds(&start_pt);
543 TruncateToImageBounds(&end_pt);
544 x_delta = end_pt.
x - start_pt.
x;
545 y_delta = end_pt.
y - start_pt.
y;
546 count = x_delta * x_step + 1;
547 for (
int x = start_pt.
x; x != end_pt.
x; x += x_step) {
548 int y = start_pt.
y +
DivRounded(y_delta * (x - start_pt.
x), x_delta);
549 total += GET_DATA_BYTE(data + wpl * y, x);
553 int y_step = y_delta > 0 ? 1 : -1;
557 start_pt.
x += offset;
559 TruncateToImageBounds(&start_pt);
560 TruncateToImageBounds(&end_pt);
561 x_delta = end_pt.
x - start_pt.
x;
562 y_delta = end_pt.
y - start_pt.
y;
563 count = y_delta * y_step + 1;
564 for (
int y = start_pt.
y; y != end_pt.
y; y += y_step) {
565 int x = start_pt.
x +
DivRounded(x_delta * (y - start_pt.
y), y_delta);
566 total += GET_DATA_BYTE(data + wpl * y, x);
577 static TBOX BoundsWithinBox(Pix* pix,
const TBOX& box) {
578 int im_height = pixGetHeight(pix);
579 Box* input_box = boxCreate(box.
left(), im_height - box.
top(),
581 Box* output_box =
nullptr;
582 pixClipBoxToForeground(pix, input_box,
nullptr, &output_box);
584 if (output_box !=
nullptr) {
585 l_int32 x, y, width, height;
586 boxGetGeometry(output_box, &x, &y, &width, &height);
589 result_box.
set_top(im_height - y);
591 boxDestroy(&output_box);
593 boxDestroy(&input_box);
601 static void TruncateBoxToMissNonText(
int x_middle,
int y_middle,
602 bool split_on_x, Pix* nontext_map,
609 im_box = BoundsWithinBox(nontext_map, box1);
611 box2.set_left(x_middle);
612 im_box = BoundsWithinBox(nontext_map, box2);
615 box1.set_bottom(y_middle);
616 im_box = BoundsWithinBox(nontext_map, box1);
618 box2.set_top(y_middle);
619 im_box = BoundsWithinBox(nontext_map, box2);
620 if (!im_box.
null_box()) box2.set_bottom(im_box.
top());
629 void TextlineProjection::IncrementRectangle8Bit(
const TBOX& box) {
630 int scaled_left = ImageXToProjectionX(box.
left());
631 int scaled_top = ImageYToProjectionY(box.
top());
632 int scaled_right = ImageXToProjectionX(box.
right());
633 int scaled_bottom = ImageYToProjectionY(box.
bottom());
634 int wpl = pixGetWpl(pix_);
635 uint32_t* data = pixGetData(pix_) + scaled_top * wpl;
636 for (
int y = scaled_top; y <= scaled_bottom; ++y) {
637 for (
int x = scaled_left; x <= scaled_right; ++x) {
638 int pixel = GET_DATA_BYTE(data, x);
640 SET_DATA_BYTE(data, x, pixel + 1);
652 void TextlineProjection::ProjectBlobs(BLOBNBOX_LIST* blobs,
654 const TBOX& nontext_map_box,
656 BLOBNBOX_IT blob_it(blobs);
657 for (blob_it.mark_cycle_pt(); !blob_it.cycled_list(); blob_it.forward()) {
662 bool spreading_horizontally = PadBlobBox(blob, &bbox);
665 middle.rotate(rotation);
666 if (rotation.
x() == 0.0f)
667 spreading_horizontally = !spreading_horizontally;
669 bbox &= nontext_map_box;
671 TruncateBoxToMissNonText(middle.x(), middle.y(), spreading_horizontally,
673 if (bbox.
area() > 0) {
674 IncrementRectangle8Bit(bbox);
682 bool TextlineProjection::PadBlobBox(
BLOBNBOX* blob,
TBOX* bbox) {
692 bool padding_horizontally =
false;
695 padding_horizontally =
true;
703 ypad = scale_factor_;
711 xpad = scale_factor_;
725 padding_horizontally =
true;
728 bbox->
pad(xpad, ypad);
738 return padding_horizontally;
743 void TextlineProjection::TransformToPixCoords(
const DENORM* denorm,
745 if (denorm !=
nullptr) {
749 pt->
x = ImageXToProjectionX(pt->
x);
750 pt->
y = ImageYToProjectionY(pt->
y);
753 #if defined(_MSC_VER) && !defined(__clang__)
754 #pragma optimize("g", off)
757 void TextlineProjection::TruncateToImageBounds(
TPOINT* pt)
const {
758 pt->
x = ClipToRange<int>(pt->
x, 0, pixGetWidth(pix_) - 1);
759 pt->
y = ClipToRange<int>(pt->
y, 0, pixGetHeight(pix_) - 1);
761 #if defined(_MSC_VER) && !defined(__clang__)
762 #pragma optimize("", on)
766 int TextlineProjection::ImageXToProjectionX(
int x)
const {
767 x =
ClipToRange((x - x_origin_) / scale_factor_, 0, pixGetWidth(pix_) - 1);
770 int TextlineProjection::ImageYToProjectionY(
int y)
const {
771 y =
ClipToRange((y_origin_ - y) / scale_factor_, 0, pixGetHeight(pix_) - 1);