15# include "config_auto.h"
18#include <allheaders.h>
51 if (scale_factor_ < 1) {
69 TBOX image_box(0, 0, pixGetWidth(nontext_map), pixGetHeight(nontext_map));
71 y_origin_ = image_box.
height();
72 int width = (image_box.
width() + scale_factor_ - 1) / scale_factor_;
73 int height = (image_box.
height() + scale_factor_ - 1) / scale_factor_;
75 pix_ = pixCreate(width, height, 8);
76 ProjectBlobs(&input_block->
blobs, rotation, image_box, nontext_map);
77 ProjectBlobs(&input_block->
large_blobs, rotation, image_box, nontext_map);
78 Image final_pix = pixBlockconv(pix_, 1, 1);
84#ifndef GRAPHICS_DISABLED
88 BLOBNBOX_IT it(blobs);
89 for (it.mark_cycle_pt(); !it.cycled_list(); it.forward()) {
110 BLOBNBOX_LIST *small_blobs)
const {
111 BLOBNBOX_IT it(blobs);
112 BLOBNBOX_IT small_it(small_blobs);
113 for (it.mark_cycle_pt(); !it.cycled_list(); it.forward()) {
119 small_it.add_to_end(it.extract());
124#ifndef GRAPHICS_DISABLED
128 int width = pixGetWidth(pix_);
129 int height = pixGetHeight(pix_);
130 Image pixc = pixCreate(width, height, 32);
131 int src_wpl = pixGetWpl(pix_);
132 int col_wpl = pixGetWpl(pixc);
133 uint32_t *src_data = pixGetData(pix_);
134 uint32_t *col_data = pixGetData(pixc);
135 for (
int y = 0;
y < height; ++
y, src_data += src_wpl, col_data += col_wpl) {
136 for (
int x = 0;
x < width; ++
x) {
137 int pixel = GET_DATA_BYTE(src_data,
x);
140 composeRGBPixel(0, 0, pixel * 15, &result);
141 }
else if (pixel <= 145) {
142 composeRGBPixel(0, (pixel - 17) * 2, 255, &result);
144 composeRGBPixel((pixel - 145) * 2, 255, 255, &result);
146 col_data[
x] = result;
149 auto *win =
new ScrollView(
"Projection", 0, 0, width, height, width, height);
150 win->Draw(pixc, 0, 0);
162 const DENORM *denorm,
bool debug)
const {
199 bool horizontal_textline,
const DENORM *denorm,
203 int parallel_gap = 0;
208 if (horizontal_textline) {
209 parallel_gap = from_box.
x_gap(to_box) + from_box.
width();
210 start_pt.
x = (from_box.
left() + from_box.
right()) / 2;
211 end_pt.
x = start_pt.
x;
213 start_pt.
y = from_box.
top();
214 end_pt.
y = std::min(to_box.
top(), start_pt.
y);
216 start_pt.
y = from_box.
bottom();
217 end_pt.
y = std::max(to_box.
bottom(), start_pt.
y);
220 parallel_gap = from_box.
y_gap(to_box) + from_box.
height();
222 start_pt.
x = from_box.
right();
223 end_pt.
x = std::min(to_box.
right(), start_pt.
x);
225 start_pt.
x = from_box.
left();
226 end_pt.
x = std::max(to_box.
left(), start_pt.
x);
228 start_pt.
y = (from_box.
bottom() + from_box.
top()) / 2;
229 end_pt.
y = start_pt.
y;
235 int perpendicular_gap = 0;
239 if (start_pt.
x != end_pt.
x || start_pt.
y != end_pt.
y) {
240 if (denorm !=
nullptr) {
245 if (abs(start_pt.
y - end_pt.
y) >= abs(start_pt.
x - end_pt.
x)) {
274 x = ImageXToProjectionX(
x);
275 y1 = ImageYToProjectionY(y1);
276 y2 = ImageYToProjectionY(y2);
280 int wpl = pixGetWpl(pix_);
281 int step = y1 < y2 ? 1 : -1;
282 uint32_t *data = pixGetData(pix_) + y1 * wpl;
284 int prev_pixel = GET_DATA_BYTE(data,
x);
286 int right_way_steps = 0;
287 for (
int y = y1;
y != y2;
y += step) {
289 int pixel = GET_DATA_BYTE(data,
x);
291 tprintf(
"At (%d,%d), pix = %d, prev=%d\n",
x,
y + step, pixel, prev_pixel);
293 if (pixel < prev_pixel) {
295 }
else if (pixel > prev_pixel) {
308 x1 = ImageXToProjectionX(x1);
309 x2 = ImageXToProjectionX(x2);
310 y = ImageYToProjectionY(
y);
314 int wpl = pixGetWpl(pix_);
315 int step = x1 < x2 ? 1 : -1;
316 uint32_t *data = pixGetData(pix_) +
y * wpl;
317 int prev_pixel = GET_DATA_BYTE(data, x1);
319 int right_way_steps = 0;
320 for (
int x = x1;
x != x2;
x += step) {
321 int pixel = GET_DATA_BYTE(data,
x + step);
323 tprintf(
"At (%d,%d), pix = %d, prev=%d\n",
x + step,
y, pixel, prev_pixel);
325 if (pixel < prev_pixel) {
327 }
else if (pixel > prev_pixel) {
344 EvaluateBoxInternal(box, denorm, debug, &grad1, &grad2,
nullptr,
nullptr);
345 int worst_result = std::min(grad1, grad2);
346 int total_result = grad1 + grad2;
347 if (total_result >= 6) {
352 if (worst_result < 0) {
382 tprintf(
"Partition hresult=%d, vresult=%d from:", hresult, vresult);
386 return hresult >= -vresult ? hresult : vresult;
415 return EvaluateBoxInternal(box, denorm, debug,
nullptr,
nullptr,
nullptr,
nullptr);
421int TextlineProjection::EvaluateBoxInternal(
const TBOX &box,
const DENORM *denorm,
bool debug,
422 int *hgrad1,
int *hgrad2,
int *vgrad1,
424 int top_gradient = BestMeanGradientInRow(denorm, box.
left(), box.
right(), box.
top(),
true);
425 int bottom_gradient =
426 -BestMeanGradientInRow(denorm, box.
left(), box.
right(), box.
bottom(),
false);
427 int left_gradient = BestMeanGradientInColumn(denorm, box.
left(), box.
bottom(), box.
top(),
true);
429 -BestMeanGradientInColumn(denorm, box.
right(), box.
bottom(), box.
top(),
false);
430 int top_clipped = std::max(top_gradient, 0);
431 int bottom_clipped = std::max(bottom_gradient, 0);
432 int left_clipped = std::max(left_gradient, 0);
433 int right_clipped = std::max(right_gradient, 0);
435 tprintf(
"Gradients: top = %d, bottom = %d, left= %d, right= %d for box:", top_gradient,
436 bottom_gradient, left_gradient, right_gradient);
439 int result = std::max(top_clipped, bottom_clipped) - std::max(left_clipped, right_clipped);
440 if (hgrad1 !=
nullptr && hgrad2 !=
nullptr) {
441 *hgrad1 = top_gradient;
442 *hgrad2 = bottom_gradient;
444 if (vgrad1 !=
nullptr && vgrad2 !=
nullptr) {
445 *vgrad1 = left_gradient;
446 *vgrad2 = right_gradient;
456int TextlineProjection::BestMeanGradientInRow(
const DENORM *denorm, int16_t min_x, int16_t max_x,
457 int16_t
y,
bool best_is_max)
const {
460 int upper = MeanPixelsInLineSegment(denorm, -2, start_pt, end_pt);
461 int lower = MeanPixelsInLineSegment(denorm, 2, start_pt, end_pt);
462 int best_gradient = lower - upper;
463 upper = MeanPixelsInLineSegment(denorm, -1, start_pt, end_pt);
464 lower = MeanPixelsInLineSegment(denorm, 3, start_pt, end_pt);
465 int gradient = lower - upper;
466 if ((gradient > best_gradient) == best_is_max) {
467 best_gradient = gradient;
469 upper = MeanPixelsInLineSegment(denorm, -3, start_pt, end_pt);
470 lower = MeanPixelsInLineSegment(denorm, 1, start_pt, end_pt);
471 gradient = lower - upper;
472 if ((gradient > best_gradient) == best_is_max) {
473 best_gradient = gradient;
475 return best_gradient;
484int TextlineProjection::BestMeanGradientInColumn(
const DENORM *denorm, int16_t
x, int16_t min_y,
485 int16_t max_y,
bool best_is_max)
const {
488 int left = MeanPixelsInLineSegment(denorm, -2, start_pt, end_pt);
489 int right = MeanPixelsInLineSegment(denorm, 2, start_pt, end_pt);
490 int best_gradient = right - left;
491 left = MeanPixelsInLineSegment(denorm, -1, start_pt, end_pt);
492 right = MeanPixelsInLineSegment(denorm, 3, start_pt, end_pt);
493 int gradient = right - left;
494 if ((gradient > best_gradient) == best_is_max) {
495 best_gradient = gradient;
497 left = MeanPixelsInLineSegment(denorm, -3, start_pt, end_pt);
498 right = MeanPixelsInLineSegment(denorm, 1, start_pt, end_pt);
499 gradient = right - left;
500 if ((gradient > best_gradient) == best_is_max) {
501 best_gradient = gradient;
503 return best_gradient;
516int TextlineProjection::MeanPixelsInLineSegment(
const DENORM *denorm,
int offset,
TPOINT start_pt,
518 TransformToPixCoords(denorm, &start_pt);
519 TransformToPixCoords(denorm, &end_pt);
520 TruncateToImageBounds(&start_pt);
521 TruncateToImageBounds(&end_pt);
522 int wpl = pixGetWpl(pix_);
523 uint32_t *data = pixGetData(pix_);
526 int x_delta = end_pt.x - start_pt.x;
527 int y_delta = end_pt.y - start_pt.y;
528 if (abs(x_delta) >= abs(y_delta)) {
533 int x_step = x_delta > 0 ? 1 : -1;
536 start_pt.y += offset;
538 TruncateToImageBounds(&start_pt);
539 TruncateToImageBounds(&end_pt);
540 x_delta = end_pt.x - start_pt.x;
541 y_delta = end_pt.y - start_pt.y;
542 count = x_delta * x_step + 1;
543 for (
int x = start_pt.x;
x != end_pt.x;
x += x_step) {
544 int y = start_pt.y +
DivRounded(y_delta * (
x - start_pt.x), x_delta);
545 total += GET_DATA_BYTE(data + wpl *
y,
x);
549 int y_step = y_delta > 0 ? 1 : -1;
553 start_pt.x += offset;
555 TruncateToImageBounds(&start_pt);
556 TruncateToImageBounds(&end_pt);
557 x_delta = end_pt.x - start_pt.x;
558 y_delta = end_pt.y - start_pt.y;
559 count = y_delta * y_step + 1;
560 for (
int y = start_pt.y;
y != end_pt.y;
y += y_step) {
561 int x = start_pt.x +
DivRounded(x_delta * (
y - start_pt.y), y_delta);
562 total += GET_DATA_BYTE(data + wpl *
y,
x);
573static TBOX BoundsWithinBox(Image pix,
const TBOX &box) {
574 int im_height = pixGetHeight(pix);
575 Box *input_box = boxCreate(box.left(), im_height - box.top(), box.width(), box.height());
576 Box *output_box =
nullptr;
577 pixClipBoxToForeground(pix, input_box,
nullptr, &output_box);
579 if (output_box !=
nullptr) {
580 l_int32
x,
y, width, height;
581 boxGetGeometry(output_box, &
x, &
y, &width, &height);
582 result_box.set_left(
x);
583 result_box.set_right(
x + width);
584 result_box.set_top(im_height -
y);
585 result_box.set_bottom(result_box.top() - height);
586 boxDestroy(&output_box);
588 boxDestroy(&input_box);
596static void TruncateBoxToMissNonText(
int x_middle,
int y_middle,
bool split_on_x, Image nontext_map,
602 box1.set_right(x_middle);
603 im_box = BoundsWithinBox(nontext_map, box1);
604 if (!im_box.null_box()) {
605 box1.set_left(im_box.right());
607 box2.set_left(x_middle);
608 im_box = BoundsWithinBox(nontext_map, box2);
609 if (!im_box.null_box()) {
610 box2.set_right(im_box.left());
613 box1.set_bottom(y_middle);
614 im_box = BoundsWithinBox(nontext_map, box1);
615 if (!im_box.null_box()) {
616 box1.set_top(im_box.bottom());
618 box2.set_top(y_middle);
619 im_box = BoundsWithinBox(nontext_map, box2);
620 if (!im_box.null_box()) {
621 box2.set_bottom(im_box.top());
630void TextlineProjection::IncrementRectangle8Bit(
const TBOX &box) {
631 int scaled_left = ImageXToProjectionX(box.left());
632 int scaled_top = ImageYToProjectionY(box.top());
633 int scaled_right = ImageXToProjectionX(box.right());
634 int scaled_bottom = ImageYToProjectionY(box.bottom());
635 int wpl = pixGetWpl(pix_);
636 uint32_t *data = pixGetData(pix_) + scaled_top * wpl;
637 for (
int y = scaled_top;
y <= scaled_bottom; ++
y) {
638 for (
int x = scaled_left;
x <= scaled_right; ++
x) {
639 int pixel = GET_DATA_BYTE(data,
x);
641 SET_DATA_BYTE(data,
x, pixel + 1);
654void TextlineProjection::ProjectBlobs(BLOBNBOX_LIST *blobs,
const FCOORD &rotation,
655 const TBOX &nontext_map_box, Image nontext_map) {
656 BLOBNBOX_IT blob_it(blobs);
657 for (blob_it.mark_cycle_pt(); !blob_it.cycled_list(); blob_it.forward()) {
658 BLOBNBOX *blob = blob_it.data();
659 TBOX bbox = blob->bounding_box();
660 ICOORD middle((bbox.left() + bbox.right()) / 2, (bbox.bottom() + bbox.top()) / 2);
661 bool spreading_horizontally = PadBlobBox(blob, &bbox);
663 bbox.rotate(rotation);
664 middle.rotate(rotation);
665 if (rotation.x() == 0.0f) {
666 spreading_horizontally = !spreading_horizontally;
669 bbox &= nontext_map_box;
671 TruncateBoxToMissNonText(middle.x(), middle.y(), spreading_horizontally, nontext_map, &bbox);
672 if (bbox.area() > 0) {
673 IncrementRectangle8Bit(bbox);
681bool TextlineProjection::PadBlobBox(BLOBNBOX *blob,
TBOX *bbox) {
691 bool padding_horizontally =
false;
692 if (blob->UniquelyHorizontal()) {
694 padding_horizontally =
true;
698 if ((blob->neighbour(
BND_ABOVE) ==
nullptr ||
699 bbox->y_gap(blob->neighbour(
BND_ABOVE)->bounding_box()) > pad_limit) &&
700 (blob->neighbour(
BND_BELOW) ==
nullptr ||
701 bbox->y_gap(blob->neighbour(
BND_BELOW)->bounding_box()) > pad_limit)) {
702 ypad = scale_factor_;
704 }
else if (blob->UniquelyVertical()) {
706 if ((blob->neighbour(
BND_LEFT) ==
nullptr ||
707 bbox->x_gap(blob->neighbour(
BND_LEFT)->bounding_box()) > pad_limit) &&
708 (blob->neighbour(
BND_RIGHT) ==
nullptr ||
709 bbox->x_gap(blob->neighbour(
BND_RIGHT)->bounding_box()) > pad_limit)) {
710 xpad = scale_factor_;
713 if ((blob->neighbour(
BND_ABOVE) !=
nullptr &&
715 (blob->neighbour(
BND_BELOW) !=
nullptr &&
719 if ((blob->neighbour(
BND_RIGHT) !=
nullptr &&
721 (blob->neighbour(
BND_LEFT) !=
nullptr &&
724 padding_horizontally =
true;
727 bbox->pad(xpad, ypad);
731 if (bbox->left() < blob->left_rule() - pad_limit) {
732 bbox->set_left(blob->left_rule() - pad_limit);
734 if (bbox->right() > blob->right_rule() + pad_limit) {
735 bbox->set_right(blob->right_rule() + pad_limit);
737 return padding_horizontally;
742void TextlineProjection::TransformToPixCoords(
const DENORM *denorm,
TPOINT *pt)
const {
743 if (denorm !=
nullptr) {
745 denorm->DenormTransform(
nullptr, *pt, pt);
747 pt->x = ImageXToProjectionX(pt->x);
748 pt->y = ImageYToProjectionY(pt->y);
751#if defined(_MSC_VER) && !defined(__clang__)
752# pragma optimize("g", off)
755void TextlineProjection::TruncateToImageBounds(
TPOINT *pt)
const {
756 pt->x = ClipToRange<int>(pt->x, 0, pixGetWidth(pix_) - 1);
757 pt->y = ClipToRange<int>(pt->y, 0, pixGetHeight(pix_) - 1);
759#if defined(_MSC_VER) && !defined(__clang__)
760# pragma optimize("", on)
764int TextlineProjection::ImageXToProjectionX(
int x)
const {
765 x =
ClipToRange((
x - x_origin_) / scale_factor_, 0, pixGetWidth(pix_) - 1);
768int TextlineProjection::ImageYToProjectionY(
int y)
const {
769 y =
ClipToRange((y_origin_ -
y) / scale_factor_, 0, pixGetHeight(pix_) - 1);
const int kWrongWayPenalty
const int kMaxTabStopOverrun
const int kParaPerpDistRatio
const int kMinLineSpacingFactor
const int kOrientedPadFactor
const int kDefaultPadFactor
UnicodeText::const_iterator::difference_type distance(const UnicodeText::const_iterator &first, const UnicodeText::const_iterator &last)
void tprintf(const char *format,...)
int IntCastRounded(double x)
int DivRounded(int a, int b)
T ClipToRange(const T &x, const T &lower_bound, const T &upper_bound)
const TBOX & bounding_box() const
bool UniquelyVertical() const
BLOBNBOX_LIST large_blobs
void DenormTransform(const DENORM *last_denorm, const TPOINT &pt, TPOINT *original) const
int y_gap(const TBOX &box) const
TDimension height() const
int x_gap(const TBOX &box) const
TDimension bottom() const
static bool WithinTestRegion(int detail_level, int x, int y)
int median_bottom() const
const TBOX & bounding_box() const
bool IsHorizontalType() const
void ConstructProjection(TO_BLOCK *input_block, const FCOORD &rotation, Image nontext_map)
bool BoxOutOfHTextline(const TBOX &box, const DENORM *denorm, bool debug) const
int DistanceOfBoxFromPartition(const TBOX &box, const ColPartition &part, const DENORM *denorm, bool debug) const
int EvaluateBox(const TBOX &box, const DENORM *denorm, bool debug) const
TextlineProjection(int resolution)
void MoveNonTextlineBlobs(BLOBNBOX_LIST *blobs, BLOBNBOX_LIST *small_blobs) const
int EvaluateColPartition(const ColPartition &part, const DENORM *denorm, bool debug) const
int DistanceOfBoxFromBox(const TBOX &from_box, const TBOX &to_box, bool horizontal_textline, const DENORM *denorm, bool debug) const
void PlotGradedBlobs(BLOBNBOX_LIST *blobs, ScrollView *win)
void DisplayProjection() const
int HorizontalDistance(bool debug, int x1, int x2, int y) const
int VerticalDistance(bool debug, int x, int y1, int y2) const
void Rectangle(int x1, int y1, int x2, int y2)